Apple, NeXT, and the publishers have tried to make the information contained in this manual as accurate and reliable
as possible, but assume no responsibility for errors or omissions. They disclaim any warranty of any kind, whether
express or implied, as to any matter whatsoever relating to this manual, including without limitation the
merchantability or fitness for any particular purpose. In no event shall they be liable for any indirect, special,
incidental, or consequential damages arising out of purchase or use of this manual or the information contained
herein. NeXT or Apple will from time to time revise the software described in this manual and reserves the right to
make such changes without obligation to notify the purchaser.
Copyright 1997 by Apple Computer, Inc., 1 Infinite Loop, Cupertino, CA 95014.
All rights reserved.
[7010.01]
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, or otherwise, without the prior written permission of the
publisher or copyright owner. Printed in the United States of America. Published simultaneously in Canada.
NeXT, the NeXT logo, OPENSTEP, Enterprise Objects, EOF, Enterprise Objects Framework, ProjectBuilder, Objective-C,
Portable Distributed Objects, Workspace Manager, Database Wizard, WEBSCRIPT, and WEBOBJECTS are trademarks
of NeXT Software, Inc. PostScript is a registered trademark of Adobe Systems, Incorporated. Windows NT is a
trademark of Microsoft Corporation. UNIX is a registered trademark in the United States and other countries, licensed
exclusively through X/Open Company Limited. ORACLE is a registered trademark of Oracle Corporation, Inc. SYBASE
is a registered trademark of Sybase, Inc. All other trademarks mentioned belong to their respective owners.
Restricted Rights Legend: Use, duplication, or disclosure by the Government is subject to restrictions as set forth in
subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 [or, if
applicable, similar clauses at FAR 52.227-19 or NASA FAR Supp. 52.227-86].
This manual describes WebObjects, version 3.5.
Written by Terry Donoghue, Katie McCormick, Matt Morse, Jean Ostrem, Kelly Toshach
With help from Eric Bailey, Craig Federighi, Patrice Gautier, Francois Jouaux, Charles Lloyd, and Dan Peknik
Developmental Editing by Jeanne Woodward
Proofread by Laurel Rezeau
Book Design by Karin Stroud
Technical illustrations by Karin Stroud
Print and Online Production Editing by Gerri Gray
Art, Production, and Editorial management by Gary Miller
Technical publications management by Greg Wilson
Contents
Table of Contents
Introduction 9
About This Book 11
Other Useful Documentation 12
Part I WebObjects Essentials
What Is a WebObjects Application? 17
The Ingredients of a WebObjects Application 19
Components 20
Template 21
Code or Script File 21
Bindings 22
Application Code 23
Session Code 23
A Note on WebObjects Classes 23
Application Directory 24
Running a WebObjects Application 25
WebObjects Adaptors 26
The WebObjects Application Executable 27
Dynamic Elements 29
Server-Side Dynamic Elements 31
How Server-Side Dynamic Elements Work 33
Binding Values to Dynamic Elements 35
Declarations File Syntax 36
Client-Side Java Components 37
Deciding When to Use Client-Side Components 37
How Client-Side Components Work 38
Common Methods 41
Action Methods 43
Initialization and Deallocation Methods 46
The Structures of init and awake 46
Application Initialization 47
Session Initialization 48
Component Initialization 49
Request-Handling Methods 50
Taking Input Values From a Request 51
Invoking an Action 52
Limitations on Direct Requests 53
Generating a Response 53
Debugging a WebObjects Application 55
Launching an Application for Debugging 57
Debugging WebScript 57
Debugging Java 57
Debugging Objective-C 58
Debugging Mixed Applications 58
Debugging Techniques 58
Writing Debug Messages 58
Using Trace Methods 59
Isolating Portions of a Page 60
Programming Pitfalls to Avoid 60
WebScript Programming Pitfalls 60
Java Programming Pitfalls 61
WebObjects Viewed Through Its Classes 63
The Classes in the Request-Response Loop 65
Server and Application Level 65
Session Level 66
Request Level 68
Page Level 69
Database Integration Level 71
v
How WebObjects Works—A Class Perspective 72
Starting the Request-Response Loop 72
Taking Values From the Request 73
Accessing the Session 74
Creating or Restoring the Request Page 76
Assigning Input Values 78
Invoking an Action 79
Generating the Response 80
How HTML Pages Are Generated 82
Component Templates 82
Associations and the Current Component 84
Associations and Client-Side Java Components 85
Subcomponents and Component References 86
Part IISpecial Tasks
State in the Page 124
State in Cookies 126
Custom State-Storage Options 128
Storing State for Custom Objects 131
Archiving Custom Objects in a Database Application 131
Archiving Custom Objects in Other Applications 132
Controlling Session State 133
Setting Session Time-Out 134
Using awake and sleep 135
Controlling Component State 135
Managing Component Resources 135
Adjusting the Page Cache Size 136
Using awake and sleep 137
pageWithName: and Page Caching 138
Client-Side Page Caching 139
Page Refresh and WODisplayGroup 140
Creating Reusable Components 91
Benefits of Reusable Components 93
Centralizing Application Resources 93
Simplifying Interfaces 96
Intercomponent Communication 98
Synchronizing Attributes in Parent and Child Components 102
Sharing Reusable Components Across Applications 104
Search Path for Reusable Components 105
Designing for Reusability 106
Managing State 109
Why Do You Need to Store State? 111
When Do You Need to Store State? 112
Objects and State 113
The Application Object and Application State 113
The Session Object and Session State 115
Component Objects and Component State 118
State Storage Strategies 120
Comparison of Storage Options 121
A Closer Look at Storage Strategies 122
State in the Server 123
Creating Client-Side Components 141
Choosing a Strategy 143
When You Have an Applet’s Source Code 144
When You Don’t Have an Applet’s Source Code 146
Deployment and Performance Issues 149
Recording Application Statistics 151
Maintaining a Log File 151
Accessing Statistics 152
Recording Extra Information 153
Error Handling 154
Automatically Terminating an Application 155
Performance Tips 156
Cache Component Definitions 156
Compile the Application 157
Control Memory Leaks 157
Limit State Storage 158
Limit Database Fetches 158
Limit Page Sizes 158
Installing Applications 159
vi
Part III WebScript
The WebScript Language 163
Objects in WebScript 165
WebScript Language Elements 166
Variables 166
Variables and Scope 167
Assigning Values to Variables 168
Methods 170
Invoking Methods 171
Accessor Methods 171
Sending a Message to a Class 172
Creating Instances of Classes 173
Data Types 174
Statements and Operators 175
Control-Flow Statements 176
Arithmetic Operators 176
Logical Operators 176
Relational Operators 176
Increment and Decrement Operators 177
Reserved Words 178
“Modern” WebScript Syntax 179
Advanced WebScript 181
Scripted Classes 181
Categories 182
WebScript for Objective-C Developers 183
Accessing WebScript Methods From Objective-C Code 185
WebScript Programmer’s Quick Reference to
Foundation Classes 187
Foundation Objects 189
Representing Objects as Strings 189
Mutable and Immutable Objects 189
Determining Equality 190
Writing to and Reading From Files 190
Writing to Files 190
Reading From Files 191
Working With Strings 191
Commonly Used String Methods 192
Creating Strings 192
Combining and Dividing Strings 193
Comparing Strings 194
Converting String Contents 194
Modifying Strings 195
Storing Strings 195
Working With Arrays 196
Commonly Used Array Methods 196
Creating Arrays 197
Querying Arrays 197
Sorting Arrays 198
Adding and Removing Objects 198
Storing Arrays 200
Representing Arrays as Strings 200
Working With Dictionaries 200
Commonly Used Dictionary Methods 201
Creating Dictionaries 202
Querying Dictionaries 203
Adding, Removing, and Modifying Entries 204
Representing Dictionaries as Strings 205
Storing Dictionaries 205
Working With Dates and Times 206
The Calendar Format 206
Date Conversion Specifiers 206
Commonly Used Date Methods 207
Creating Dates 207
Adjusting a Date 207
Representing Dates as Strings 208
Retrieving Date Elements 208
Index 211
vii
Introduction
About This Book
WebObjects is an object-oriented environment for developing and
deploying World Wide Web applications. A WebObjects application runs on
a server machine, receives requests from a user’s web browser on a client
machine, dynamically generates the appropriate HTML page in response
to those requests, and returns that page to the user. WebObjects provides
you with a web application server, prebuilt application components, and a
suite of tools for rapid development of World Wide Web applications.
WebObjects is flexible enough to suit the needs of any web programmer.
You can, for instance, write code using one of three programming languages:
Java, Objective-C, or WebScript. You can write simple WebObjects
applications in a matter of minutes. And if your programming task is more
complex, WebObjects still makes it as easy as possible by performing
common web application tasks automatically and by allowing you to reuse
objects you’ve written for other applications.
This book describes concepts that you’ll need to know when writing a
WebObjects application. Programmers of all skill levels will find all or part
of the information in this book useful. To help you find what you are looking
for, this book is organized into three parts:
•Part 1, “WebObjects Essentials,” is for programmers who are new to
WebObjects.
Part 1 covers basic concepts that are required knowledge for even the
simplest of WebObjects programs. It describes what a WebObjects
application is and what pieces a WebObjects application contains. It
describes how to debug applications and provides a checklist of hardto-find errors. The final chapter in Part 1, “WebObjects Viewed
Through Its Classes,” provides an in-depth description of the classes
used in all WebObjects applications and explains how those classes
process HTTP requests.
•Part 2, “Special Tasks,” is for intermediate-to-advanced WebObjects
programmers.
Part 2 provides information that you can ignore until you understand
the basic WebObjects concepts explained in Part 1. It describes how
you can design components for reuse inside of other components, how
a WebObjects application manages and stores state, how to create
11
Introduction
client-side Java components that behave like dynamic elements, and how
to design an application for deployment and improved performance.
•Part 3, “WebScript,” is for programmers who want to use a scripting
language.
WebScript is a scripting language provided with WebObjects for rapid
application development. Use of WebScript is entirely voluntary—you can
write applications using Java or Objective-C if you prefer. Part 3 describes
WebScript’s syntax and also describes how to use the Foundation
framework when writing an application using WebScript.
There are no prerequisites for learning WebObjects; however, it does help if you
understand object-oriented programming concepts and are familiar with either
Java or Objective-C. If you aren’t familiar with Java or Objective-C, you might
want to pay special attention to Part 3 of this book. Part 3 provides a very brief
introduction to object-oriented concepts—enough to get you started. Later,
you’ll want to round out your knowledge either by reading the book
Oriented Programming and the Objective-C Language
or any other book on object-
Object-
oriented programming.
Other Useful Documentation
If you’re new to WebObjects programming, begin by reading the book Getting
Started With WebObjects
a WebObjects application as well as the basic concepts behind WebObjects.
If you want to write an application that accesses a database, you’ll need to use
Enterprise Objects in conjunction with WebObjects. And although database
access is covered in the tutorials in
also want to read the
depth information.
Other valuable information can be found online. To access online
documentation, use the WebObjects Home Page. The WebObjects Home Page
is in your web server’s document root, and you can access it at this URL:
In particular, WOHomePage’s Documentation link gives you access to some
books that are available only online:
12
. It contains tutorials that teach the mechanics of creating
Getting Started With WebObjects, you’ll probably
Enterprise Objects Framework Developer’s Guide for more in-
Other Useful Documentation
•WebObjects Tools and Techniques describes the development tools
WebObjects Builder and Project Builder and shows how to use them to
create WebObjects applications.
Serving WebObjects describes how to administer and deploy WebObjects
•
applications after you’ve written them.
•The WebObjects Class Reference provides a complete reference to the
classes in the WebObjects framework. Reference material is provided
for both the Java and Objective-C languages.
•The
Dynamic Elements Reference documents the dynamic elements
provided with WebObjects and shows examples of how to use them.
•The Client-Side Applet Controls Reference lists and describes the client-
side Java components provided with WebObjects.
13
WEBOBJECTS ESSENTIALS
Chapter 1
What Is a WebObjects Application?
WebObjects is a product that makes it easy for you to write dynamic webbased applications (or
WebObjects applications). Before you start
programming, however, you need to understand what a WebObjects
application is.
This chapter answers the question what is a WebObjects application in two
ways: first by showing you the pieces of a simple WebObjects application,
and then by explaining what happens when a WebObjects application runs.
In the rest of Part 1, you’ll learn how to construct these pieces and you’ll get
more in-depth information about how they work.
Read this chapter if you want a very high-level overview. The rest of this
book provides much more detailed information about how WebObjects
applications work and how to write one.
When you’re ready to start programming, read the book
WebObjects
. It provides a series of tutorials that help you understand the tasks
and tools involved in writing a WebObjects application.
The Ingredients of a WebObjects Application
WebObjects applications reside within a directory named
web server’s document root (
<DocumentRoot>
/WebObjects/Examples/WebScript
These are WebObjects application projects, provided with the WebObjects
package as examples that you can use when learning WebObjects. These
examples range from simple to highly complex. For now, you might want to
focus on two of the simplest applications, named
When you look inside any of these project directories, you may see these
pieces:
•
directories, which are called components. Components are dynamic
.wo
HTML pages.
•An application code file (
applicationwide resources.
•A session code file (
resources.
<DocumentRoot>
Application.wos
Session.wos
), which creates and manages sessionwide
Getting Started With
/WebObjects
WebObjects
). Look in
in your
, and you’ll see several directories.
HelloWorld
and
Visitors
.
), which creates and manages
•Standard project files, such as makefiles.
19
Chapter 1What Is a WebObjects Application?
The following sections describe the WebObjects application ingredients in
more detail.
Components
To write a WebObjects application, you create components and then connect
them. A
behavior. Usually a component represents an entire page, so the word “page” is
used interchangeably with the word “component.” However, remember, that
not all components represent an entire page. For example, a component might
represent only a header or footer of a page, and you can nest that component
inside of a component that does represent the entire page.
component is a web page, or a portion of one, that has both content and
Each component is located in its own directory, named
Component
generally contains these parts:
•A template that specifies how the component looks
•Code that specifies how the component acts
•Bindings that associate the component’s template with its code
Main.wo
Figure 1 shows the contents of the
is almost always the name of the first page of a WebObjects
example. (
Main.wo
application.) In this example, the
template in the form of an HTML file (
declarations file (
Main.wod), which contains the bindings between the template
component from the HelloWorld
Main.wo
component contains three files: a
), the code file (
Main.html
Main.wos
and the code.
Main.wo
Main.html
Main.wodMain.wos
, and
.wo
), and the
Figure 1. The Contents of a WebScript Component Directory
Typically, components contain some form of the three files shown in Figure 1;
however, any given component might contain more or fewer files. For example,
components whose code is written in a compiled language do not contain code
20
The Ingredients of a WebObjects Application
files. A component may not need a code file at all; it may need only a
template file and a declarations file. Another component might have a code
file but no template file or declarations file. Plus, if you create a component
using Project Builder or WebObjects Builder, you’ll get a fourth file,
Component
, which contains API that should be made public to other
.api
components.
The next three sections describe more completely these template, code,
and declarations files.
Template
You use a template (
look. This file typically contains static HTML elements (such as
<P>) along with some dynamic elements. Dynamic elements are the basic
building blocks of a WebObjects application. They link an application’s
behavior with the HTML page shown in the web browser, and their
contents are defined at runtime.
An HTML template can also contain a reference to another component
(called a
reusable component or subcomponent) that represents a portion of an
HTML page. This reference behaves just like a reference to a dynamic
element.
Main.html
) to specify how the page you’re creating should
<H1> or
Code or Script File
You use the code file (
actions. The attributes are called
actions are called
With WebObjects, you can write your code file in one of three programming
languages: Java, Objective-C, or WebScript. Java is the language of choice
for many people; others prefer Objective-C. Because both of these
languages require compiling, they aren’t as well suited to rapid prototyping
as a scripting language is. For this reason WebObjects provides a scripting
language named WebScript, described in the chapter “The WebScript
Language” (page 163). You may have noticed that the examples directory
mentioned previously offers examples in all three languages.
Java support is not available on the Mach or HP-UX platform.
Note:
The
Main.wo
component shown in Figure 1 uses a WebScript file to define its
behavior. (The
or Objective-C, the code file resides at the same level as the
Main.wos
) to define your component’s attributes and
variables or instance variables, and the
methods.
extension signifies WebScript.) If you want to use Java
.wos
Main.wo directory
21
Chapter 1What Is a WebObjects Application?
as shown in Figure 2. (In Project Builder, Java and Objective-C code files are
shown under Classes instead of with the component under Web Components.)
HelloWorld
Main.wo
Main.html
Figure 2. Location of Code File for Java Component
Main.wod
Main.java
You can mix languages. It’s common to use WebScript to write your interface
logic (that is, the files described in this chapter) and use Java or Objective-C to
write your business logic. Many simple applications are written entirely in
WebScript. Some programmers prototype using WebScript and then create a
compiled version of the same application to improve performance.
Bindings
You use a declarations file (Main.wod) to define the bindings, or mapping, between
the methods and variables you defined in your code and the dynamic elements
in your template. For example in the HelloWorld application, the HTML
template for the Main component contains two dynamic elements. The
declarations file specifies that the first dynamic element represents a text field
whose value maps to the
user types a name in the text field, WebObjects assigns it to the
variable. The declarations file also specifies that the second dynamic element is
a submit button and that when the user clicks the button, WebObjects invokes
sayHello method.
the
visitorName variable in the component’s script. When the
visitorName
22
The Ingredients of a WebObjects Application
Application Code
In addition to having one or more components, your application can also
include application code. In application code, you declare and initialize
application variables and perform tasks that affect the entire application.
The application code file is named Application and its extension is based on
the programming language you use to create it (
Application.m).
Application.wos, Application.java, or
Session Code
Sessions are periods during which one user is accessing your application.
Because users on different clients may be accessing your application at the
same time, a single application may have more than one session accessing it
at a time (see Figure 3). Each session has its own copy of the components
that its user has requested.
component
session
application
Figure 3. Application, Sessions, and Components
You perform tasks that affect a single session and store variables that persist
throughout a session in the session code file. The session code file is named
Session and has the appropriate extension (
Session.wos, Session.java, or Session.m).
A Note on WebObjects Classes
True to its name, WebObjects is an object-oriented environment for writing
web applications. Therefore, when you write a component, an application
file, or a session file, you are really writing a class. This is true whether you
use WebScript, Java, or Objective-C. You can learn about these classes and
23
Chapter 1What Is a WebObjects Application?
where they are used by reading the chapter “WebObjects Viewed Through Its
Classes” (page 63).
Components are subclasses of a class named WOComponent. For example, in
Figure 1 the component directory creates a WOComponent subclass named
Main. Application files create subclasses of a class named WOApplication, and
session files create subclasses of a class named WOSession.
WOComponent, WOApplication, and WOSession are defined, along with other
classes, in the WebObjects Framework in
NeXT_ROOT
/NextLibrary/Frameworks/WebObjects.framework. (NeXT_ROOT is an
environment variable defined at installation time. On Windows NT systems, it
C:\NeXT by default. On Mach systems, the NeXT_ROOT environment variable is
is
undefined, but you can think of it as being the root directory
In Java, WebObjects classes have different names. The names shown previously
are the WebScript names. (Objective-C uses the same names as WebScript.) In
Java, WOComponent is called Component, WOApplication is WebApplication,
and WOSession is WebSession. The Java classes are contained in the package
next.wo.
Note: This book generally uses the WebScript names for classes and methods.
Usually, you can easily discern the Java name from the WebScript name, and
vice versa. The following table tells you how to do so.
/.)
WebScript/Objective-C NameJava Name
Class namesWOClassClass (or next.wo.Class)
Zero-argument methods
Single-argument methods method:method
Multiple-argument
methods
methodmethod
methodWithArg1
:arg2:methodWithArg1
Where the mapping is not obvious, this book notes both the Java and
WebScript/Objective-C names.
Application Directory
When you build a WebObjects application project, the result is a directory with
the extension
the component files, copies of the script files (if any), and any resources that the
application or the HTTP server needs access to. When you’re ready to deploy
an application to your users, you use the
24
.woa. This directory contains the application executable, copies of
.woa to run the application.
Running a WebObjects Application
Now you’ve learned what a WebObjects application looks like and seen the
pieces that you’ll have to write. The next section tells you how to run a
WebObjects application.
Running a WebObjects Application
WebObjects applications run on a web server. Your users connect to a
WebObjects application using web browsers that they run on their own
(client) machines. How does a user start a WebObjects application, and how
does the application communicate with the browser?
Users run a WebObjects application using a Uniform Resource Locator
(URL) similar to the one shown in Figure 4. (Of course, you’d probably
provide a button or a link on a static web page that would take users to this
URL rather than forcing your users to type such a long string.)
To start your own applications, you open a command shell window, go to the
directory that contains your application, and enter the application
command. WebObjects starts up your application, opens the web browser,
and enters the URL in the web browser for you. For example, to start the
Java version of HelloWorld, go to the directory
<DocRoot>
the executable file, and enter
/WebObjects/Examples/Java/HelloWorldJava/HelloWorldJava.woa, which contains
HelloWorld on the command line. On Windows
NT, you can simply navigate to this directory in the Explorer and doubleclick the
HelloWorld.exe file.
When you run a WebObjects application, it communicates with the web
browser through the chain of processes shown in Figure 5.
25
Chapter 1What Is a WebObjects Application?
Web
Browser
Figure 5. Chain of Communication Between the Browser and Your WebObjects Application
HTTP
Server
WebObjects
Adaptor
WebObjects
Application
Here is a brief description of these processes:
•
An HTTP server. Any HTTP server that uses the Common Gateway Interface
(CGI), the Netscape Server API (NSAPI), or the Internet Server API
(ISAPI).
A WebObjects adaptor. A WebObjects adaptor connects WebObjects applications
•
to the web by acting as an intermediary between web applications and
HTTP servers.
A WebObjects application executable. The application executable receives incoming
•
requests and responds to them, usually by returning a dynamically
generated HTML page.
Two of these, WebObjects adaptors and WebObjects application executables,
are described next.
WebObjects Adaptors
A WebObjects adaptor receives requests from the server, repackages the
requests in a standard format, and forwards them to an appropriate WebObjects
application (see Figure 6).
HTTP
Server
Figure 6. The Role of a WebObjects Adaptor
Server
Interface
WebObjects
Adaptor
WebObjects
Interface
WebObjects
Application
All WebObjects adaptors communicate with WebObjects applications in the
same way, but they communicate with HTTP servers using whatever interface
is provided by a particular server. For example, the WebObjects CGI adaptor
uses the Common Gateway Interface, the Netscape Interface adaptor uses the
Netscape Server API (NSAPI), and the Internet Server adaptor uses the
26
Running a WebObjects Application
Internet Server API (ISAPI). Thus, WebObjects adaptors can take
advantage of server-specific interfaces but still provide server
independence.
By default, WebObjects uses the WebObjects CGI adaptor. The Common
Gateway Interface is supported by all HTTP servers, so you can use the
CGI adaptor with any server—including those that are publicly available.
As demands on performance increase, switch to one of the other adaptors
with a server that supports the corresponding API (Netscape Server API or
Internet Server API). Such servers are capable of dynamically loading the
adaptor, eliminating the overhead of starting a new process for each request.
As shown in Figure 7, the communication between the adaptor and the
HTTP server occurs inside a single process.
Netscape
Commerce
Server
Figure 7. The Netscape Interface Adaptor
Netscape
Interface
Adaptor
WebObjects
Interface
WebObjects
Application
The online document Serving WebObjects describes how to configure a
WebObjects adaptor.
The WebObjects Application Executable
An application executable is an executable file, provided by you or by
WebObjects, that receives incoming requests from the adaptor and
responds to them, usually by returning a dynamically generated HTML
page.
If your application is written entirely in WebScript, it can use the default
application executable, NeXT_ROOT
provided as part of the WebObjects package. If your application contains
compiled code, you build your own executable and use it in place of
WODefaultApp.
WebObjects applications are event driven, but instead of responding to
mouse and keyboard events, they respond to HTTP requests. A
WebObjects application receives a request, responds to it, and then waits
for the next request. The application continues to respond to requests until
it terminates. During each cycle of this request-response loop, the application
extracts the user input from the request, invokes an action if one is
/NextLibrary/Executables/WODefaultApp,
27
Chapter 1What Is a WebObjects Application?
associated with the user’s action, and generates a response—usually an HTML
page (see Figure 8).
Web BrowserHTTP ServerWebObjects AdaptorWebObjects Applications
Request
Page
Request
Request
1. Take Values
From Request
Request
Component
User
performs
an action
Response
Page
User
sees the
next page
Response
Figure 8. The Request-Response Loop
Response
2. Invoke Action
3. Generate
Response
Returns
response
component
Response
Component
Generates
response
page
28
Chapter 2
Dynamic Elements
In the previous chapter, you learned that a WebObjects application is made
up of components, which in turn are made up of dynamic elements.
Dynamic elements are the basic building blocks of a WebObjects
application. They link an application’s behavior with the HTML page
shown in the web browser, and their contents are defined at runtime.
There are two types of dynamic elements that you can place in a
component:
•Server-side dynamic elements
•Client-side Java components
This chapter describes each of these types and tells you how to decide
when to use them. Before reading it, you should be familiar with the
concepts presented in the previous chapter. To learn the mechanics of using
dynamic elements, see the online book WebObjects Tools and Techniques.
Server-Side Dynamic Elements
Server-side dynamic elements are the simplest type of element to create
and are supported by all web browsers. Needless to say, they are the most
commonly used.
Server-side dynamic elements produce HTML at runtime. This HTML is
composed of the same HTML elements you use when you’re creating a
static web page. Like static elements, dynamic elements display formatted
text, images, forms, hyperlinks, and active images. WebObjects provides
several dynamic elements. For a complete list, see the online book DynamicElements Reference.
For an example of dynamic elements in action, look at the CyberWind
sample application. It’s located in
where
<DocRoot>
is your web server’s document root. When you run
<DocRoot>
/WebObjects/Examples/Java/CyberWindJava,
CyberWind, its first page contains a list of hyperlinks, shown in Figure 9.
Figure 9. CyberWind Main Page
31
Chapter 2Dynamic Elements
This list is not hard-coded into the page. Instead, it is produced by several
dynamic elements. Figure 10 shows how this same part of the page looks in
WebObjects Builder.
Figure 10. CyberWind Main Page in WebObjects Builder
The elements shown in Figure 10 are a WORepetition, a WOHyperlink, and a
WOString. The WORepetition element corresponds to a
That is, it iterates through a list of items and, for each item in that list, prints its
contents. In this example, the contents are a WOHyperlink and a WOString.
The WOHyperlink is a hyperlink whose destination is determined at runtime,
and the WOString is a string whose contents are determined at runtime.
When you run CyberWind, the WORepetition walks through an array of strings
that the component’s code supplies. For each item in the array, it displays a
hyperlink whose text is the text of the string item in the array. In this array, there
are two strings—“See surfshop information” and “Buy a new sailboard”—so the
WORepetition creates two hyperlinks, each containing the appropriate text.
for loop in C code.
As the name implies, server-side dynamic elements operate entirely on the
server (see Figure 11). That is, when a server-side dynamic element is asked to
draw itself, it returns HTML code that should form part of a page, the page is
constructed, and then the entire page is sent from the server to the client. Later
in this chapter, you’ll learn about client-side components, which transport
values and state from the server to the client and then draw themselves on the
client machine.
32
Server-Side Dynamic Elements
Response PageWeb Browser
<HTML>
</HTML>
ServerClient
Dynamic Element
<...>
Dynamic Element
<...>
Dynamic Element
<...>
Figure 11. Server-Side Dynamic Elements
How Server-Side Dynamic Elements Work
To learn how server-side dynamic elements work, look once more at the
Main page of the CyberWind example in WebObjects Builder. If you switch
over to raw mode, you see that the Main page contains this HTML code in
its template:
Choose between the following menu options:<BR><BR>
<WEBOBJECT NAME="OPTION_REPETITION">
<WEBOBJECT NAME="OPTION_LINK">
<WEBOBJECT NAME="OPTION_NAME"></WEBOBJECT>
</WEBOBJECT>
</WEBOBJECT>
Each WEBOBJECT tag denotes the position of a dynamic element. Notice
that the tag specifies only where the dynamic element should go; it does not
specify the dynamic element’s type. The type is specified in the
OPTION_REPETITION:WORepetition {
list = allOptions;
item = currentOption
};
OPTION_LINK:WOHyperlink {
action = pickOption
};
OPTION_NAME:WOString {
value = currentOption
};
.wod file:
33
Chapter 2Dynamic Elements
In the .wod file, each element is identified by name and then its type is specified.
The outermost dynamic element in the HTML file
(OPTION_REPETITION) defines a WORepetition, the next element is a
WOHyperlink, and the innermost element is a WOString.
Each type specification is followed by a list of attributes. Dynamic elements
define several attributes that you bind to different values to configure the
element for your application. Usually, you bind attributes to variables or
methods from your component’s code (see Figure 12).
TEXTFIELD : WOTextField {
value = aName;
}
Button : WOSubmitButton {
action = recordThis;
}
id aName;
- recordThis{
...
}
Record
Figure 12. Dynamic Element Bindings
action
value
In the CyberWind example, the Main component binds to two attributes of
WORepetition:
WORepetition should iterate over. The
list and item. The list attribute specifies the list that the
item attribute specifies a variable whose
value will be updated in each iteration of the list (like an index variable in a
loop). CyberWind’s Main component binds the
allOptions and the item attribute to a variable named currentOption.
list attribute to an array named
for
The WOHyperlink has an
pickOptions. The action attribute specifies a method that should be invoked when
the user clicks the link. In this case, the
action attribute, which is bound to a method called
pickOptions method determines which link
the user clicked and then returns the appropriate page.
34
Server-Side Dynamic Elements
Finally, the WOString element defines a value attribute, which specifies the
string you want displayed. This
variable, which is also bound to the
you’ll recall,
currentOption is updated with each iteration that the
WORepetition makes. So, for each item in the
WORepetition’s
list attribute), the WORepetition updates the currentOption
value attribute is bound to the currentOption
item attribute of the WORepetition. As
allOptions array (assigned to the
variable to point to that item and then the WOString prints it on the page.
Binding Values to Dynamic Elements
In the CyberWind example, all of the dynamic elements are bound to
variables and methods from the component that contains them (the Main
component). It’s common to bind to variables and methods declared
directly in the current component; however, you can bind to any value that
the component can access.
This means, for instance, that you can bind to variables from the application
or session object because the WOComponent class declares two instance
variables,
current session. Look at CyberWind’s Footer component in WebObjects
Builder. This component displays, among other information, the date and
time the CyberWind application was started.This date is stored in the
application object, not in the Footer component. The Footer component’s
.wod file contains this declaration:
application and session, which point to the current application and the
To retrieve a value from this binding, WebObjects uses key-value coding, a
standard interface for accessing an object’s properties either through
methods designed for that purpose or directly through its instance variables.
With key-value coding, WebObjects sends the same message
takeValue:forKey:, or takeValue in Java) to any object it is trying to access. Key-
(
value coding first attempts to access properties through accessor methods
based on the key’s name.
For example, to resolve the binding for the WOString element in the
Footer component using key-value coding, WebObjects performs the
following steps:
•It resolves the value for the
application in the component object.
In this case, WOComponent (Component in Java) defines the
application key by looking for a method named
application
method, which returns the WOApplication object (WebApplication in
Java).
35
Chapter 2Dynamic Elements
•It resolves the value for the upSince key by looking for a method named upSince
in the application object.
If the method is not found, it looks for an
case, the
•It resolves the value for the
description in the upSince object.
Because
upSince instance variable is defined in the application’s code file.
description key by looking for a method named
upSince is a date object, it defines a description method, which prints
upSince instance variable. In this
the object’s value as a string.
Note: The Java equivalent of the description method is toString, but you must use
the WebScript name for methods and literals in the
.wod file even though
the application is written in Java.
Here are the general rules for binding dynamic element attributes:
•You must bind to a variable or method accessible by the current component.
(You can also bind to constant values.)
•If you bind to a method, the method must take no arguments. (If you need
to bind to a method that takes arguments, you can wrap it inside of a method
that doesn’t take arguments.)
•You can bind to any key for objects that define keys.
For example, dictionary objects store key-value pairs. Suppose you declare
person dictionary that has the keys name, address, and phone. These keys aren’t
a
really instance variables in the dictionary, but because WebObjects
accesses values using key-value coding, the following binding works:
myString : WOString { value = person.name };
•You must use the Objective-C names for methods and literals.
Even if your entire application is written in Java, you must use the
Objective-C names for methods and for literals. For example, you must use
YES instead of true, NO instead of false, and description instead of toString.
Declarations File Syntax
As you’ve seen, the .wod file specifies nearly all of the information necessary to
create a dynamic element. Because you usually create dynamic elements and
their bindings using WebObjects Builder, you normally don’t have to worry
about the syntax of the
36
.wod file. However, here it is for the curious:
Client-Side Java Components
elementName
Notice that the last attribute/value pair before a closing brace (}) does not
end with a semicolon (;).
As described in the previous section, value can be a constant, variable, or
method. It can also be a string of messages joined by a dot, similar to the
Java syntax for sending messages but without the parentheses. For
example:
application.upSince.description
Client-Side Java Components
Instead of using server-side dynamic elements, you can create Java applets
that run on the client. Typically, applets are downloaded to the client once
and then have virtually no communication with the server. This is not the
case with the client-side Java components provided with WebObjects.
While these applets run on the client, they continuously synchronize their
states with objects on the server. Client-side components can also trigger
action methods on the server. For this reason, they may be said to work in
virtually the same way as server-side dynamic elements.
To add a client-side component to your application, you drag it from the
palette provided in WebObjects Builder. For a list of the client-side Java
components that WebObjects provides, see the online book Client-SideApplet Controls Reference.
:
elementType
{
attribute
=
value;attribute
=
value
};
Deciding When to Use Client-Side Components
You should use client-side components whenever you want greater control
over the appearance of your application. In general, client-side components
have these advantages over server-side dynamic elements:
•Client-side components define a state-synchronization phase that does
not reload the page.
As you learned in the first chapter, WebObjects applications are event
driven. The events that trigger actions are HTTP requests. A
WebObjects application receives an HTTP request from the client,
processes it, and returns a response page. That is, the only
communication that takes place between the client and the
WebObjects application on the server results in a page being redrawn
(or a new page being generated).
37
Chapter 2Dynamic Elements
When client-side components are used, an HTTP request can result in
either the resynchronization of state or the return of a new page. Thus,
state can be synchronized without the page having to be redrawn (see
Figure 13).
Client-Side
Web Browser
HTML
Figure 13. Client-Side Java Components
Component
Association
attributes
WOApplet
Dynamic Element
ServerClient
•Client-side components are more flexible than server-side dynamic
elements.
Server-side dynamic elements always generate HTML, which means that
they are limited to what HTML looks like and what HTML can do. You
can create client-side components that look like just about any imaginable
control: a dynamic calendar, a spreadsheet, or a graphing tool. To learn how
to create a client-side component, see the chapter “Creating Client-Side
Components” (page 141).
The disadvantage to using client-side components is that they require a Javaenabled browser. Thus, you can use client-side components only when you can
be certain all of your users will have Java-enabled browsers. If you can’t
guarantee this, you should use server-side dynamic elements.
How Client-Side Components Work
A client-side component is really just a special case of a particular server-side
dynamic element named WOApplet. You use WOApplet when you want to
include any Java applet in a WebObjects application. The difference between a
client-side component and other Java applets is that client-side components can
communicate with the server.
38
Client-Side Java Components
When you look at client-side component’s bindings in the .wod file, it looks
like this example:
Like any other server-side dynamic element, the WOApplet’s definition
contains a list of attributes bound to constants or variables in the
component’s code.
code attribute specifies which client-side component this WOApplet
The
should download. The
codebase attribute specifies the path of the component
relative to your web server’s document root. (For the provided client-side
components, this path is always
The
archive attribute specifies .jar files that should be preloaded onto the
/WebObjects/Java.)
client machine. If you don’t use this attribute, the applet downloads Java
.class files from the server one by one as it needs them. With the archive
attribute, you can package all necessary Java classes into archive files, and
they are downloaded once. However, only web browsers that have Java 1.1
support can use
your users use browsers that don’t support
.jar files. Because Java 1.1 is fairly new, there’s a good chance
.jar files. All of the provided
client-side components are packaged in a single archive file named
woextensions.jar.
The
associationClass attribute differentiates the client-side components from
any other applet you might include in your application. This attribute
specifies an object (a subclass of
next.wo.client.Association) that the component
uses to communicate with the application on the server. The Association
object can get and set component state and cause methods to be invoked in
the server when actions are triggered in the client. For the WebObjectsprovided client-side components, this attribute is always
next.wo.client.SimpleAssociation. If you create your own client-side components,
you provide your own Association subclass.
The final attribute,
component. The Association object assigns the value of the
stringValue, is an attribute specific to the TextFieldApplet
inputString
variable to be the value of the text field on the client and keeps the two
objects in sync so that they always have the same value.
39
Chapter 3
Common Methods
The methods that you write for your WebObjects application provide the
behavior that makes your application unique. Because you are writing
subclasses of WOApplication, WOSession, and WOComponent (in Java,
WebApplication, WebSession, and Component), you inherit the methods
provided by those classes. These inherited methods take care of the details
of receiving HTTP requests and generating responses. However, you’ll
sometimes find that you need to override some of the inherited methods to
perform certain tasks.
This chapter describes the types of methods that you generally write in a
WebObjects application. These types are:
•Action methods
•Initialization and deallocation methods
•Request-handling methods
In cases where you override existing methods, those methods are invoked
at standard, predictable times during the application’s request-response
loop (the main loop for a WebObjects application). For background on the
request-response loop, see the chapter “WebObjects Viewed Through Its
Classes” (page 63).
As you’re writing methods, refer to the class specifications for
WOApplication, WOSession, and WOComponent to learn which messages
you can send to these objects. The class specifications are in the online
book WebObjects Class Reference.
Action Methods
An action method is a method you associate with a user action—for instance,
clicking a submit button, an active image, or a hyperlink. To associate your
method to a user action, you map it to a dynamic element that has an
attribute named
action. (In the examples just given, the dynamic elements
associated with the user actions are WOSubmitButton, WOActiveImage, or
WOHyperlink.) When the user performs the associated action, your
method is invoked.
For example, in the HelloWorld example application (in
<DocRoot>
/WebObjects/Examples/WebScript/HelloWorld, where
<DocRoot>
is your web
server’s document root), the submit button is mapped to a method named
sayHello in the Main component. When users see this page, they type in a
43
Chapter 3Common Methods
name and click the button. This initiates the application’s request-response
loop, and
sayHello is invoked.
Action methods take no arguments and return a page (component) that will be
packaged with an HTTP response. For example, the
new page named Hello and sends that page the name the user has typed into
the text field.
//WebScript HelloWorld Main.wos
- sayHello
{
id nextPage;
nextPage = [WOApp pageWithName:@"Hello"];
[nextPage setVisitorName:visitorName];
return nextPage;
}
If you’re programming in Java, you can look at the HelloWorldJava example,
which is identical to HelloWorld but written in Java. Its
this:
//Java HelloWorld Main.java
public Component sayHello() {
In this example, the component Main is used to generate the page that handles
the user request, and the component Hello generates the page that represents
the response. Main is the request component or the request page, and Hello is the response component or the response page.
sayHello method creates a
sayHello method looks like
It’s common for action methods to determine the response page based on user
input. For example, the following action method returns an error page if the user
has entered an invalid part number (stored in the variable
partnumber); otherwise,
it returns an inventory summary:
// WebScript example
- showPart {
id errorPage;
id inventoryPage;
if ([self isValidPartNumber:partnumber]) {
errorPage = [[self application] pageWithName:@"Error"];
[errorPage setErrorMessage:@"Invalid part number %@.",
aName = ""; // clear the text field
}
return null;
}
Note: Always return nil (null) in an action method instead of returning self (this).
Returning
Returning
nil uses the request component as the response component.
self uses the current component as the response component. At
first glance, these two return values seem to do the same thing. However, if
the action method is in a component that’s nested inside of the request
component, a return value of
self will make the application try to use the
nested component, which represents only a portion of the page, as the
response component. This, most likely, is not what you want. Therefore, it
is safer to always return
nil.
45
Chapter 3Common Methods
Initialization and Deallocation Methods
Like all objects, WOApplication, WOSession, and WOComponent implement
initialization methods (or constructors in Java). Because most subclasses require
some unique initialization code, these are the methods that you override most
frequently. In WebScript, the initialization methods are
initialization methods are the constructor for the class and
Both
init and awake perform initialization tasks, but they are invoked at different
times during an object’s life. The
once, when the object is first created. In contrast,
of each cycle of the request-response loop that the object is involved in. Thus,
it may be sent several times during an object’s life.
init and awake. In Java, the
awake.
init message (or the constructor in Java) is sent
awake is sent at the beginning
Complementing
awake and init are the sleep and dealloc methods. These methods
let objects deallocate their instance variables and perform other clean-up tasks.
sleep method is invoked at the end of each cycle of the request-response
The
loop, whereas the
The
dealloc method is used primarily for Objective-C objects. Standard dealloc
methods in Objective-C send each instance variable a
dealloc method is invoked at the end of the object’s life.
release message to make
sure that the instance variables are freed. WebScript and Java, because they
have automatic garbage collection, usually make a deallocation method
unnecessary. If you find it necessary, you can implement
finalize in Java.
dealloc in WebScript and
The Structures of init and awake
The init method must begin with an invocation of super’s init method and must
end by returning
- init {
}
Likewise, in Java, the constructor must begin with an invocation of the
superclass’s constructor (as with all Java classes):
public Application() {
}
The awake method has no such structure. In it, you don’t need to send a message
super or return anything.
to
self.
[super init];
/* initializations go here */
return self;
super();
/* initializations go here */
46
Initialization and Deallocation Methods
- awake {
/* initializations go here */
}
public void awake () {
/* initializations go here. */
}
Application Initialization
The application init method is invoked only once, when the application is
launched. You perform two main tasks in the application’s
This method begins by calling the application’s init method. Then, it
initializes the application variable
lastVisitor to be the empty string. (The
application has just started, so there has been no last visitor.) Finally, it sets
the application to terminate after it has been running 2 hours.
This example sets the application time-out value. You might want to do
other configurations in the application object’s
init method as well. For
example, you can control how pages and components are cached and how
state is stored. For more information, read the chapter “Managing State”
(page 109).
The application’s
request-response loop. Therefore, in the
awake method is invoked at the start of every cycle of the
awake method, you perform
anything that should happen before each and every user request is
processed. For example, the DodgeDemo example application keeps track
of the number of requests the application has received. It increments and
logs that number at the top of the request-response loop:
A session object is created each time the application receives a request from a
new user. An application may have multiple sessions running concurrently. The
session ends when a session time-out value is reached.
In the session object’s
init method, you set the session’s time-out value and
initialize variables that should have unique values for each session. For example,
in the CyberWind application, each session keeps track of which number it is.
These values are changed in the session object’s
//From CyberWind Session.wos
- init {
[super init];
[self setTimeOut:120]; // session idle time is 2 minutes.
[[self application] setSessionCount:[[self application]
The session object’s awake method is invoked each time the user associated with
the session makes a new request. After the application object has performed its
awake method, it restores the appropriate session object and sends it the
own
awake message too.
The CyberWind application keeps track of the number of requests per session.
It increments the number in the session’s
awake method.
- awake {
requestCount++;
}
48
Initialization and Deallocation Methods
Component Initialization
A component object’s init method is invoked when the component object is
created. Just how often a particular component object is created depends on
whether the application object is caching pages. For more information, see
“WebObjects Viewed Through Its Classes” (page 63). If page caching is
turned on (as it is by default), the application object generally creates the
component object once and then restores that object from the cache every
time it is involved in a user request. If page caching is turned off, the
component object is freed at the end of the request-response loop.
Note: The pageWithName: method shown in the section “Action Methods”
(page 43) always creates a new component object, even if page caching is
turned on.
A component object’s
For example, in the EmployeeBook example, the
init to initialize the departments component variable:
// WebScript EmployeeBook Department.wos
id departments;
In general, you use init to initialize component instance variables instead of
awake. The reason is that init is invoked only at component initialization time,
whereas
awake is potentially invoked much more than that. If, however, you
want to minimize the amount of state stored between cycles of the requestresponse loop, you might choose to initialize component instance variables
awake and then deallocate them in sleep (by setting them to nil in WebScript
in
49
Chapter 3Common Methods
or null in Java). For more information, see the chapter “Managing State”
(page 109).
Request-Handling Methods
Request-handling is performed in three phases, which correspond to three
methods that you can override:
•Taking input values from the request (
takeValuesFromRequest)
•Invoking the action (
•Generating a response (
invokeActionForRequest:inContext: or invokeAction)
appendToResponse:inContext: or appendToResponse)
takeValuesFromRequest:inContext: or
Each of the methods is implemented by WOApplication, WOSession, and
WOComponent. In each phase, WOApplication receives the message first, then
sends it to the WOSession, which sends it to the WOComponent, which sends
it to all of the dynamic element and component objects on the page.
The request-handling methods handle three types of objects:
•A request object (WORequest or Request in Java) is passed as an argument
in the first two phases. This object represents a user request. You can use it
to retrieve information about the request, such as the method line, request
headers, the URL, and form values.
•A context object (WOContext or Context in Java) is passed as an argument
in all three phases. This object represents the current context of the
application. It contains references to information specific to the application,
such as the path to the request component’s directory, the version of
WebObjects that’s running, the application name, and the request page’s
name.
•A response object (WOResponse in Java) is passed in the final phase. This
object encapsulates information contained in the generated HTTP
response, such as the status, response headers, and response content.
You should override these methods if you need to perform a task that requires
this type of information or you need access to objects before or after the action
method is invoked. For example, if you need to modify the header lines of an
HTTP response or substitute a page for the requested page, you would override
appendToResponse:inContext:.
50
Request-Handling Methods
As you implement request-handling methods, you must invoke the
superclass’s implementation of the same methods. But consider where you
invoke it because it can affect the request, response, and context
information available at any given point. In short, you want to perform
certain tasks before
super is invoked and other tasks after super is invoked.
Taking Input Values From a Request
The takeValuesFromRequest:inContext: method is invoked during the first phase of
the request-response loop, immediately after all of the objects involved in
the request have performed their
concludes, the request component has been initialized with the bindings
made in WebObjects Builder.
awake methods. When this phase
Override
takeValuesFromRequest:inContext: when you want to do one of the
following:
•Access information from the request or context object.
•Perform postprocessing on user input.
In the first case, you can place your code before the message to
second case, you must place your code after the message to
example, the following implementation of
takeValuesFromRequest:inContext:
super. In the
super. For
records the kinds of browsers—user agents—from which requests are
made:
// Java example
public void takeValuesFromRequest(Request request, Context context) {
super.takeValuesFromRequest(request, context);
address = street + city + state + zipCode;
}
51
Chapter 3Common Methods
Invoking an Action
The second phase of the request-response loop involves
invokeActionForRequest:inContext:
object until it is handled by the dynamic element associated with the user action
(typically, a submit button, a hyperlink, and active image, or a form).
invokeActionForRequest:inContext: if you want to return a page other than the one
Use
requested. This scenario might occur if the user requests a page that has a
dependency on another page that the user must fill out first. The user might, for
example, finish ordering items from a catalog application and want to go to a
fulfillment page but first have to supply credit card information.
. WebObjects forwards this method from object to
The following example, implemented in
Session.wos, returns a “CreditCard” page
if the user hasn’t supplied this information yet:
// WebScript example
- invokeActionForRequest:request inContext:context {
id creditPage;
id responsePage = [super invokeActionForRequest:request
When the application receives a request for a new page (say, a fulfillment page),
the session object determines whether or not the user has supplied valid creditcard data by checking the value of its
verified variable. If the value of verified is NO,
the session object returns the “CreditCard” component. As shown in the
52
Request-Handling Methods
following action method, the “CreditCard” component sets the verified
session variable to YES when the user has supplied valid credit information
and returns the user to the original request page to try again.
Users can access any page in an application without invoking an action. All
they need to do is type in the appropriate URL. For example, you can
access the second page of HelloWorld without invoking the
opening this URL:
When a WebObjects application receives such a request, it bypasses the
user-input (
invokeActionForRequest:inContext:) phases because there is no user input to store
(
takeValuesFromRequest:inContext:) and action-invocation
and no action to invoke. As a result, the object representing the requested
page—Hello in this case—generates the response.
sayHello action by
By implementing security mechanisms in
invokeActionForRequest:inContext:, you
can prevent users from accessing pages without authorization, but only if
those pages are not directly requested in URLs. To prevent users from
directly accessing pages in URLs, you must implement another strategy.
Generating a Response
The appendToResponse:inContext: method is invoked in the final phase of the
request-response loop, during which the application generates HTML for
the response page. You can override this method to add to the response
content or otherwise manipulate the HTTP response. For example, you
can add or modify the HTTP headers as in the following example:
After you invoke super’s appendToResponse:inContext:, the application generates the
response page. At this point you could do something appropriate for the end of
the request. For example, the following implementation terminates the current
session:
For more details on each phase of the request-response loop, read the chapter
“WebObjects Viewed Through Its Classes” (page 63).
54
Chapter 4
Debugging a WebObjects Application
In the previous chapters, you learned the pieces of a WebObjects
application and the kinds of methods you need to write. Once you’ve put
together an application, you should debug it to make sure it runs properly.
The techniques you use to debug vary according to the languages you’ve
used to write the application.
This chapter describes how to debug WebScript code, Java code, and
Objective-C code in a WebObjects application. When you debug, you’ll be
using the Project Builder application. To learn how to use Project Builder,
see the online book WebObjects Tools and Techniques.
Before you debug, it’s a good idea to test your installation and verify that it
works properly. If you haven’t already done so, follow the instructions in the
online document Post-Installation Information.
Launching an Application for Debugging
You debug WebObjects applications using Project Builder, as described in
the online book WebObjects Tools and Techniques. The executable you launch
differs based on which language you used to write the application. This
section tells you how to begin a debugging session for WebObjects
applications written in each of the three available languages: WebScript,
Java, and Objective-C.
Debugging WebScript
To debug WebScript code, you rely on log messages and trace statements
described in the section “Debugging Techniques” (page 58).
If you’ve written an application entirely in WebScript, you typically debug
it by running NeXT_ROOT
Builder launch panel, as described in WebObjects Tools and Techniques. When
you do, the output from the debugging and trace statements is displayed in
the launch panel.
/NextLibrary/Executables/WODefaultApp from the Project
Debugging Java
The debugging strategy for Java applications is very similar to the strategy
for debugging WebScript applications. Because the WebObjects Java
bridge is incompatible with
WebObjects. Instead, you can use the methods described in the section
“Debugging Techniques” (page 58) as well as
Build the executable for your project using Project Builder, then launch that
jdb, no Java debugger is supported for
System.out.println statements.
57
Chapter 4Debugging a WebObjects Application
executable in the launch panel. Output from the debugging methods appears in
the launch panel.
Debugging Objective-C
If all or part of your application is written in Objective-C, you can use the gdb
debugger in Project Builder. For more information on debugging an ObjectiveC application with Project Builder, see Project Builder’s online help.
If your application contains WebScript code as well as Objective-C code, you
debug the WebScript portion using
statements as described in “Debugging Techniques” (page 58).
Debugging Mixed Applications
When you build a WebObjects application project, the result is a .woa file
package inside of the project directory. You may notice that this file package
contains all of the application’s components (including scripted components),
and all other resources need to run the application, as well as the application
executable itself.
When you’re debugging an application, the executable uses the components
from the project directory instead of those in the
ignore the components placed inside of the
make a change, change the component in the project directory. When you run
an application, it checks to see if its
(that is, a directory that contains a file named
takes its scripted components from the project directory. This way, you can
make any necessary changes to your scripts in Project Builder, and (once you
have saved the scripts) your application automatically picks up your changes
without your having to rebuild.
logWithFormat: and WOApplication trace
.woa package, so you can safely
.woa package. When you need to
.woa package is inside of a project directory
PB.project). If it is, the application
Debugging Techniques
To debug WebScript and Java code, you rely primarily on log messages and trace
statements that write to standard output. This section describes the statements
you can include in your code to help you debug.
Writing Debug Messages
The method logWithFormat: (in Java, logString) writes a formatted string to standard
error (
58
stderr).
Debugging Techniques
In WebScript and Objective-C, logWithFormat: works like the printf() function in
C. This method takes a format string and a variable number of additional
arguments. For example, the following code excerpt prints the string “The
value of myString is Elvis”:
myString = @"Elvis";
[self logWithFormat:@"The value of myString is %@", myString];
When this code is parsed, the value of myString is substituted for the
conversion specification
data type of the variable being substituted is an object (that is, of the
%@. The conversion character @ indicates that the
id data
type).
Because in WebScript all variables are objects, the conversion specification
you use must always be
specifications for primitive C data types such as
%@. Unlike printf(), you can’t supply conversion
%d, %s, %f, and so on. (If you
do, you might see the address of the variable rather than its value.)
In Java, the equivalent of
WebApplication objects. Instead of using
logWithFormat: is logString, and you can send it only to
printf specifications, it uses
concatenation. Here’s how you’d write the same lines of code in Java:
myString = "Elvis";
application().logString("The value of myString is " + myString);
Perhaps the most effective debugging technique is to use logWithFormat: to
print the contents of
variables. For example, this statement at the end of the
HelloWorld’s
[self logWithFormat:@"The contents of self in sayHello are %@", self];
self. This prints the values of all of your component
sayHello method in
Main.wos:
produces output that resembles the following:
The contents of self in sayHello are
<<WOScriptedClass(/WebObjects/Examples/WebScript/HelloWorld.woa/Main
.wo/Main): 0x8cb08 name=Main subcomponents=0x0> visitorName=frank>
Here’s how you’d write the same line of code in Java:
application().logString("The contents of this in sayHello are "
+ this.toString());
Using Trace Methods
WOApplication (in Java, WebApplication) provides trace methods that log
different kinds of information about your running application. These
59
Chapter 4Debugging a WebObjects Application
methods are useful if you want to see all or part of the call stack. The following
table describes the trace methods:
MethodDescription
– trace:Enables all tracing.
– traceAssignments: Logs information about all assignment statements.
– traceStatements:Logs information about all statements.
– traceScriptedMessages:Logs information when an application enters and exits a scripted method.
– traceObjectiveCMessages: Logs information about all Objective-C methods invocations.
The output from the trace methods appears in Project Builder’s launch panel.
You use the trace methods wherever you want to turn on tracing. Usually, you do
this in the
init method (or constructor) of a component or the application:
If a component is producing unexpected HTML output, you can try to isolate
the problem by printing small portions of the page at a time. Use HTML
comments (
<!--) to comment out all but the suspect portion of the page and
reload the component. Verify that this portion works as you intend it to. Reduce
the size of the commented out portion of the page until more and more of the
page is visible in the browser. Continue until you have found the offending area.
Programming Pitfalls to Avoid
This section describes some things to look out for as you debug your application.
WebScript Programming Pitfalls
Because WebScript looks so much like Objective-C and C, it’s easy to forget that
WebScript isn’t either of these languages. When you’re debugging WebScript
code, watch out for the following tricky spots:
60
Programming Pitfalls to Avoid
•WebScript supports only objects that inherit from NSObject. As most
objects inherit from NSObject, this limitation is easy to overlook.
Notably, EOFault does not inherit from NSObject, so you cannot use it
in WebScript code.
•The == operator is supported only for NSNumber objects. If you use
== to compare two objects of any other class, the operator compares the
addresses of the two objects, not the values of the two objects. To test
the equality of two objects, use the
NSString *string1, *string2;
// WRONG!
if (aString1 == aString2) ...
// Right
if ([aString1 isEqual:string2]) ...
isEqual: method.
•The postincrement and postdecrement operators are not supported. If
you use them, you won’t receive an error message. Instead, they behave
like preincrement and predecrement operators.
i = 0;
if (i++ < 1 )
// This code never gets executed.
•WebScript always evaluates both sides of a Boolean expression (such as
&& and ||). You should make sure that the second half of an expression
does not produce an error.
// WRONG! produces a divide by 0 if a is 0.
if ((a == 0) || (b / a) > 5) ...
For more information, see the chapter “The WebScript Language” (page
163).
Java Programming Pitfalls
When debugging Java code, watch out for the following tricky spots:
•You can’t define multiple constructors or overloaded methods for the
classes WebApplication, WebSession, Component, or any other class
that originates as an Objective-C class. For example, the following code
causes your application to crash:
•The pageWithName method creates the page by looking up and instantiating
the component class that has the same name as the argument you provide
pageWithName. For this reason, your subclass of Component shouldn’t be
to
given a package name. For example, if you create a component named
MyPage.wo and place its Java file in the package myClasses.web, pageWithName won’t
find the
MyPage.class file.
•Java is a more strictly typed language than is Objective-C or WebScript. If
you’re more familiar with Objective-C, you’ll find that you need to cast the
return types frequently. For example, suppose you define a method named
verify in the file Session.java and you want to invoke that method from a
component’s Java file. To do so, you must cast the return type of the
component’s
// From a component’s Java file.
((Session)session()).verify();
session method as in the following:
By definition, session returns a WebSession object. Because WebSession
does not define a method named
cast the return value of
verify, your code won’t compile unless you
session to your WebSession subclass.
62
Chapter 5
WebObjects Viewed Through Its Classes
The Classes in the Request-Response Loop
As you learned at the end of the first chapter, WebObjects applications
respond to HTTP requests from the server and return responses in the
form of dynamically generated HTML pages. The main loop of a
WebObjects application, in which the application performs this work, is
called the request-response loop. You have a very broad understanding of
how this works: the web browser sends a request to the HTTP server,
which forwards it to the WebObjects adaptor, which translates it into a form
that a WebObjects application can understand. For the response, the
process is reversed.
This chapter describes in much greater detail what happens during the
request-response loop. It does so by describing the request-response loop
as WebObjects views it: as a communication between objects. In this
chapter, you learn about the objects that are involved at each level of the
loop, each object’s duty during each part of the request-response loop, and
the way these objects generate an appropriate HTML page in response to
the user request.
In the chapter “Common Methods” (page 41), you learned some of the
methods that are invoked during the request-response loop, and you
learned about cases where you might want to override these methods. As
you write more complex WebObjects applications, it becomes necessary to
know exactly what happens at each point in the processing of an HTTP
request and the generation of an HTTP response. You should read this
chapter to learn that level of detail. You can also refer to the class
specifications in the online book WebObjects Class Reference.
The Classes in the Request-Response Loop
The request-response loop begins when an incoming message (URL) from
a client web browser is handled by the HTTP server. This section starts at
that point and then dives into the request-response loop layer by layer,
telling you which classes get involved, and at which point. Later sections
walk you through the sequence of events that happen during one cycle of
the request-response loop and the sequence of events for generating an
HTML page.
Server and Application Level
At the server and application level, the request-response loop looks like
that shown in Figure 14.
65
Chapter 5WebObjects Viewed Through Its Classes
Request
HTTP server
Figure 14. Request-Response Loop: Application and Server Level
adaptor
application
Response
The HTTP server sends a request to the application’s adaptor. The adaptor
packages the incoming HTTP request in a form the WebObjects application
can understand and forwards it to the application. The application initiates and
manages the process of request handling and returns the completed response to
the adaptor, which gives it to the HTTP server in a form the server can
understand.
Two classes are involved at this level:
•WOAdaptor (in Java, Adaptor)
Defines the interface for objects mediating the exchange of data between
an HTTP server and a WebObjects application. This is an abstract class.
•WOApplication (in Java, WebApplication)
Receives requests from the adaptor and initiates and coordinates the
request-handling process, after which it returns a response to the adaptor.
WOApplication also creates dynamic elements “on the fly” and manages
adaptors, sessions, application resources, and components.
Session Level
At the session level, the request-response loop looks like that shown in
Figure 15.
66
The Classes in the Request-Response Loop
Request
session
store
HTTP server
Figure 15. Request-Response Loop: Session Level
adaptor
Response
application
session 1
session 2
The objects dedicated to session management ensure that state with
sessionwide scope persists between cycles of the request-response loop.
Two classes are involved at this level:
•WOSession (in Java, WebSession)
Encapsulates the state of a session. WOSession objects persist
between the cycles of the request-response loop. WOSession objects
store (and restore) the pages of a session, the values of session
variables, and any other state that components want to persist
throughout a session. The number of pages stored by the session
object is dependent on the page-cache size set in WOApplication.
Setting the page-cache size is described in the chapter “Managing
State” (page 109). Each session object is identified by a unique session
ID, which is reflected in the URL.
•WOSessionStore (in Java, SessionStore)
Provides the strategy or mechanism through which WOSession objects
are made persistent. A WOSessionStore object stores session objects in
the server or in the page (which can include Netscape cookies), and
restores them upon request by the application.
67
Chapter 5WebObjects Viewed Through Its Classes
When a user makes an initial request to a WebObjects application, the
application creates a session object (WOSession). At the end of the requestresponse cycle, the application stores the state-bearing session object using the
facilities of WOSessionStore. With each subsequent cycle of the requestresponse loop for that user, the application restores the state of the session at the
beginning of the cycle and stores it again at the end of the cycle. To learn more
about how to use WOSessionStore, see the chapter “Managing State”
(page 109).
Request Level
The request-response cycle has three phases, the first for transferring userentered data to the objects associated with the request page, the second for
invoking an action method, and the third for generating and returning the
response. Figure 16 shows how WebObjects requests are handled at the
transaction level.
Stores essential data about an HTTP request, such as header information,
form values, HTTP version, host and page name, and session, context, and
sender IDs.
session store
session 1
session 1
68
The Classes in the Request-Response Loop
•WOResponse (in Java, Response)
Stores and allows the modification of HTTP response data, such as
header information, status, and HTTP version. It also provides
convenience methods for appending HTML and simple textual data
to the content of the response (that is, the response page).
•WOContext (in Java, Context)
Provides access to the objects involved in the current cycle, such as the
current request, response, session, and application objects. It also
stores the component (either the current page or one of its
subcomponents) to which the elements of the page make reference
when they “push and pull” values through association. See “How
HTML Pages Are Generated” (page 82) for an explanation. The
WOContext object acts as a “cursor,” traversing the object graph
during each phase of the request-response loop. The WOContext for a
cycle is identified by a unique context ID, which appears in the URL.
You rarely need to work directly with WORequest, WOResponse, and
WOContext yourself. At the beginning of the request-response loop, the
WOAdaptor and WOApplication objects create instances of these three
classes. The application initiates each phase of the request-response loop
by sending the messages
invokeActionForRequest:inContext:, and appendToResponse:inContext: (in Java,
takeValuesFromRequest, invokeAction, and appendToResponse). It passes in the
takeValuesFromRequest:inContext:,
WORequest, WOResponse, and WOContext objects as arguments to one
or more of these methods. From these objects, the components, dynamic
elements, and other objects involved in the cycle get essential information.
See “How WebObjects Works—A Class Perspective” (page 72) for more on
the mechanics of request handling.
Page Level
At the page level, objects of many classes (most of them private) work
together to compose the HTML content of response pages (see Figure 17).
Many of the same objects also set their variable values from data entered
into request pages and respond to user actions.
69
Chapter 5WebObjects Viewed Through Its Classes
HTTP server
adaptor
Response
Request
application
session store
transaction 1
request
page
transaction 2
request
page
session 1
response
page
response
page
session 2
Figure 17. Request-Response Loop: Page Level
Two major branches of these objects descend from WOElement:
WOComponent objects, which represent components, and
WODynamicElement objects, which represent dynamic HTML elements on
the page. For details on how this happens and for more on these classes, see
“How HTML Pages Are Generated” (page 82).
Four classes are involved at this level:
•WOComponent (in Java, Component)
Represents a integral, reusable page (or portion of a page) for display in a
web browser.
•WOElement (in Java, Element)
Declares the three request-handling methods:
invokeActionForRequest:inContext:, and appendToResponse:inContext:. WOElement is an
takeValuesFromRequest:inContext:,
abstract class. Each node in an object graph, which represents the HTML
elements of a component and their relationships, is an object that inherits
from WOElement.
•WODynamicElement (in Java, DynamicElement)
An abstract class for subclasses that generate particular dynamic elements.
70
The Classes in the Request-Response Loop
•WOAssociation (in Java, Association)
Knows how to find and set a value by reference to a key. Instance
variables and action methods of dynamic elements are instances of this
class.
Database Integration Level
Database integration is handled mainly by classes in the Enterprise Objects
Framework (see Figure 18). The Enterprise Objects Framework converts
operations on objects to database operations on records, thereby allowing
your WebObjects application to interact with a database in an objectoriented manner.
Request
session store
HTTP server
HTTP serverHTTP server
adaptor
application
request 1
session 1
Response
Figure 18. Request-Response Loop: Database Access
Two classes are involved at this level:
•WODisplayGroup (in Java, DisplayGroup)
Performs fetches, queries, creations, and deletions of records from one
table in the database. WODisplayGroup is a sort of bridge between the
request
page
WODisplay
Group
EOEditing
Context
response
page
71
Chapter 5WebObjects Viewed Through Its Classes
dynamic elements on your page and the objects in the Enterprise Objects
Framework.
•EOEditingContext (in Java, next.eo.EditingContext)
Manages a graph of objects fetched from a database. The objects represent
tables, rows, and columns in the database.
When a WebObjects application accesses a database, one or more of the
components in the application contain one or more WODisplayGroup objects.
The session object provides access to an EOEditingContext object that is used,
for example, when changed data is saved to the database. Each session uses an
EOEditingContext to manage graphs of objects fetched from a database and to
ensure that all parts of an application remain synchronized. For read-only
applications, you can customize WOSession to return a per-application
EOEditingContext.
For more information on how the WebObjects and Enterprise Objects classes
interact, see the Enterprise Objects Developer’s Guide.
How WebObjects Works—A Class Perspective
You’ve now had a brief introduction to the classes used in WebObjects. This
section describes the sequence of events that happen during a cycle of the
request-response loop—how the application starts up, what happens when it
receives an HTTP request, and how it processes that request.
Starting the Request-Response Loop
A WebObjects application can start up in one of two ways: automatically, when
it receives a request (autostarting), or manually, when it’s run from the command
line. Either way, its entry point is the same as that of any C program: the
function. In a WebObjects application,
main is usually very short. Its job is to
create and run the application.
main function begins by creating an autorelease pool that’s used for the
The
automatic deallocation of objects that receive an
autorelease message. It then calls
a function that loads the Java Virtual Machine (VM) if necessary.
The next step is to create a WOApplication (or WebApplication) object. This
seems fairly straightforward, but in the
init method or constructor the application
creates and stores, in an instance variable, one or more adaptors. These adaptors,
all instances of a WOAdaptor subclass, handle communication between an
72
main
How WebObjects Works—A Class Perspective
HTTP server and the WOApplication object. The application first parses
the command line for the specified adaptors (with necessary arguments); if
none are specified, as happens when the application is autostarted, it creates
a suitable default adaptor.
run method initiates the request-response loop. When run is invoked,
The
the application sends
registerForEvents to each of its adaptors to tell them to
begin receiving events. Then the application begins running in its run loop.
The autorelease pool is released and recreated immediately before the
run
message is sent. Releasing the autorelease pool at this point releases any
temporary variables created during initialization of the application class.
Creating a new autorelease pool before sending
run ensures that all variables
created while running the application will be released. The last message
releases the autorelease pool, which in turn releases any object that has
been added to the pool since the application started running.
In the rest of this section, we look at what happens during one complete
cycle of the request-response loop.
Taking Values From the Request
The first phase of the request-response loop (see Figure 19) synchronizes
the state of the request component with the HTML page as submitted by
the user. In this phase, the appropriate dynamic elements extract the values
that users enter and the choices they make in the request page and assign
them to declared variables.
For example, if the user clicked a checkbox, the dynamic element that
represents that checkbox must be set to the “checked” state. In other
words, the
element must be set to YES.
checked attribute of the appropriate WOCheckbox dynamic
73
Chapter 5WebObjects Viewed Through Its Classes
Application
init
awake
Create WOSession
or
Restore WOSession
Create page
or
Restore page
takeValuesFrom
Request:inContext:
Session
init
awake
takeValuesFrom
Request:inContext:
Gets request page
and stores reference
to it.
Request Page
init
awake
takeValuesFrom
Request:inContext:
Gets template for
page.
Page Template
takeValuesFrom
Request:inContext:
Each dynamic element
of the the template that
accepts input responds
to takeValuesFrom
Request:inContext:.
If a user-entered value
belongs to any element,
the element sets the
related value of the
related WOAssociation.
Figure 19. Taking Values From the Request
A cycle of the request-response loop begins when the WOAdaptor receives an
incoming HTTP request. The adaptor object packages this request in a
WORequest and forwards this object to the application object in a
handleRequest:
message. Upon receiving this message, the application object does the
following:
1. It creates the WOResponse and WOContext objects that will be needed.
2. It invokes its own
awake method.
3. It determines which session and which request page are associated with the
request, as described next.
Accessing the Session
The application determines whether to create a new session or access an
existing session by searching the request URL (which was passed in as an
argument to the
one for the session, the request URL looks like the URL shown in Figure 20.
74
handleRequest: method) for a session ID. If the request is the first
How WebObjects Works—A Class Perspective
HTTP
server name
adaptor
name
http://ursa/cgi-bin/WebObjects/SomeWebApp
name of the Web server's
cgi-bin directory
Figure 20. URL to Start a WebObjects Application
application path relative to
<
DocRoot
>/ WebObjects
This URL does not contain a session ID, so the application object creates a
new session by performing the following steps:
1. It sends itself a
2. As part of the
createSession message.
createSession method, it sends the init message or the
constructor message to the WOSession (or WebSession) class to create
a new session object.
3. It sends the
awake message to the session object.
If the request is part of an existing session, the request URL looks like the
one shown in Figure 21.
This URL contains all of the information necessary to restore the state of
the existing session. The session ID comes right after the application name
in the URL. Because sessions are designed to protect the data of one user’s
75
Chapter 5WebObjects Viewed Through Its Classes
transactions from that of another, session IDs must not be easily predicted or
faked. To this end, WebObjects uses randomly generated 32-digit integers as
session IDs. (You can also override WOSession’s
another security scheme if you’d like.)
The application keeps existing, active sessions in the WOSessionStore object.
The application object uses the session ID to retrieve the appropriate session
from the session store (see Figure 22). The appropriate session object is then
sent the
Figure 22. Associating a Request With a Session Object
Creating or Restoring the Request Page
After the session receives the awake message, the next step is to find the request
page. Each request received by a WebObjects application is associated with one
of the application’s pages—the request page. The request page is usually the
response page from the last request. (The response page shows the result, or
output, of the request.)
If the user has just begun a new session (that is, if the request URL looks like
the one shown in Figure 20), the user has not requested a specific page.
Therefore, the application object creates a new instance of the WOComponent
76
How WebObjects Works—A Class Perspective
class for the page named “Main.” The application object performs the
following steps to create a component:
1. It looks in the runtime system for a WOComponent subclass that has
the same name as the request page (in this case, “Main”). If it finds such
a class with the same name, it creates an instance of that class.
2. If the application object fails to find a class in the runtime system, it
looks for a scripted component with the name of the request page.
When it finds the
.wo directory, it creates a component object using a
unique WOComponent subclass for the scripted component and makes
the scripted code the class implementation.
3. It invokes the WOComponent subclass’s
4. It invokes the WOComponent subclass’s
init method or constructor.
awake method to prepare it for
the request.
If the request is made from an established session, the application object
attempts to retrieve the request page from its cache. By default, an
application caches component (or page) instances once they’re created,
primarily to facilitate backtracking: when users backtrack, they’re revisiting
pages restored by the application. The request URL contains the
information needed to retrieve the page from the cache (see Figure 21).
This information includes the page name and a context ID.
The component may not be in the cache for one of three reasons:
•The page-caching feature is turned off.
•The request is the first for that page during the session.
•The user has backtracked beyond the page cache limit.
If the component is not in the cache, the application object creates the
component using the procedure described above. If the component is in the
cache, it sends the component the
awake message.
Note that to retrieve the page from the cache, a context ID is required in
addition to the page name. The context ID identifies a page as it existed at the end of a particular request-response loop. Why is the context ID necessary?
Imagine you’re accessing a WebObjects application that lets you subscribe
to various publications. You navigate from the site’s home page to the order
page, where you select a publication, and then you go to the customer
information page and fill in your address. After submitting this information,
you navigate back to the home page. Next, you decide to enter a
77
Chapter 5WebObjects Viewed Through Its Classes
subscription for a friend. You follow the process a second time, selecting a
different publication and entering your friend’s address.
At this point, within a single session with the subscriptions application, you’ve
accessed the same pages twice, entering different information each time. Let’s
say that you now realize that you made a mistake in your own address, so you
backtrack to that page, change the address, and resubmit the information. It’s
important that the new address information is submitted to the customer
information page as it existed during the first order so that the revised
information can be associated with the right publication order.
WebObjects associates a different context ID (again, a randomly generated
integer—to maintain security) with each request-response loop cycle. A request
to a session includes both the name of request page and a context ID so the
session object can locate, from its cache of page instances, the appropriate one
to handle the request.
Assigning Input Values
At this point, the application, session, and component objects have been created
(if necessary) and awakened so that they are ready for the request. The next step
is to extract user-entered values and assign them to variables. Here is the basic
sequence of events in preparing for a request:
1. The application object sends
takeValuesFromRequest) to itself; its implementation simply invokes the session
object’s
takeValuesFromRequest:inContext: method.
2. The session sends the
takeValuesFromRequest:inContext: (in Java,
takeValuesFromRequest:inContext: message to the request
component.
3. The component, in its implementation of
takeValuesFromRequest:inContext:, gets its
template and forwards the message to the template’s root object. A template
is an object graph that represents the static HTML elements, dynamic
HTML elements, and subcomponents that together compose the page
associated with a component instance.
4. All dynamic elements in the page template and in the templates of
subcomponents receive the
takeValuesFromRequest:inContext: message. If one of
these elements “owns” a user-entered value, it responds to the message by
storing the value in the appropriate variable defined in the request
component’s declarations file.
78
How WebObjects Works—A Class Perspective
For more on how components are associated with templates, and on how
HTML elements participate in request-handling, see “How HTML Pages
Are Generated” (page 82).
Invoking an Action
In the second phase of the request-response loop (see Figure 23), the
application first determines which dynamic element the user has clicked (or
otherwise activated) and then has that element trigger the appropriate
action method in the request component. This method returns the responsepage—the component responsible for generating an HTTP response. If the
user has not triggered an action, the request component is used as the
response component.
Application
invokeActionFor
Request:inContext:
response page
Figure 23. Invoking an Action
Session
invokeActionFor
Request:inContext:
Gets request page
and stores reference
to it.
Request Page
invokeActionFor
Request:inContext:
Gets template for
component
Invokes action
method, which returns
component of
response page.
Page Template
invokeActionFor
Request:inContext:
Target dynamic
element responds by
returning value of
action.
Here is the basic sequence of events for invoking an action:
1. The application object sends
invokeAction) to itself; its implementation simply invokes the session
object’s
2. The session sends
invokeActionForRequest:inContext: method.
invokeActionForRequest:inContext: to the request component.
3. The component, in its implementation of
invokeActionForRequest:inContext: (in Java,
invokeActionForRequest:inContext:,
gets the template of the component and forwards the message to the
template’s root object.
4. Suitable dynamic elements in the request-page template and in
subcomponent templates handle the
invokeActionForRequest:inContext:
79
Chapter 5WebObjects Viewed Through Its Classes
message and invoke the appropriate action method in the request
component. This action method returns the response page.
To be suitable, an element must be able to respond to user actions (a
WOSubmitButton or a WOActiveImage, for example). Each of these
elements evaluates the invoked action to determine if it “owns” it.
For more on how components are associated with templates and on how HTML
elements participate in request-handling, see “How HTML Pages Are
Generated” (page 82).
Generating the Response
In the final phase of request-response loop (see Figure 24), the response page
generates an HTTP response. Generally, the response contains a dynamically
generated HTML page. Each element (static and dynamic) that makes up the
response page appends its HTML code to the total stream of HTML code that
will be interpreted by the client browser.
Application
appendToResponse:
inContext:
Completes response
Saves session
sleep
’
’
’
dealloc
Session
appendToResponse:
inContext:
Gets response
page and stores
reference to it.
saves page.
sleep
’
’
’
’
’
dealloc
appendToResponse:
inContext:
Response Page
Gets template for
page
Sends sleep
to all "
awake
components
’
’
’
’
’
dealloc
"
Page Template
appendToResponse:
inContext:
Dynamic and static
HTML elements
append their content
to response.
Figure 24. Generating the Response
Here is the basic sequence of events for generating a response:
80
How WebObjects Works—A Class Perspective
1. The application object stores the response component indicated by the
action method’s return value. (This action method was invoked during
the second phase of the request-response loop.)
2. If the response component is different from the request component,
application sends the
awake message to the response component.
3. The application object sends
appendToResponse:inContext: to itself; its
implementation simply invokes the session object’s
appendToResponse:inContext: method.
4. The session pushes the response component onto the WOContext
stack and sends the response component the
appendToResponse:inContext:
message.
5. The response component, in its implementation of
appendToResponse:inContext:, gets the template for the component and sends
appendToResponse:inContext:
to the template’s root object.
6. All static and dynamic HTML elements in the response-page
template, and in subcomponent templates, receive the
appendToResponse:inContext: message. In it, they append to the content of the
response the HTML code that represents them. For dynamic
elements, this code includes the values assigned to variables.
7. When control returns to the session object, the session object asks the
WOStatisticsStore to record statistics about the response.
WOStatisticsStore sends the session a
descriptionForResponse:inContext:
message. The session, in turn, sends the response component
descriptionForResponse:inContext: message. By default, this method returns the
response component’s name.
After the response has been generated, but before returning the response
to the adaptor, the application object concludes request handling by doing
the following:
1. It causes the
sleep method—the counterpart of awake—to be invoked in
all components involved in the cycle (request, response, and
subcomponents). As described in the chapter “Managing State”
(page 109), in the
sleep method, objects can release resources that don’t
have to be saved between cycles.
2. It requests the session object to save the response page in the page
cache.
3. It invokes the session object’s
sleep method.
81
Chapter 5WebObjects Viewed Through Its Classes
4. It saves the session object in the session store.
5. It invokes its own
When an Objective-C object is about to be destroyed, its
invoked at an undefined point in time after a cycle (indicated by the vertical
ellipses in Figure 24). In the
instance variables. In WebScript, this usually happens implicitly; you therefore
usually don’t need to implement the
Java, objects have automatic garbage collection, so this deallocation step is
unnecessary.
How HTML Pages Are Generated
So how exactly are request-handling messages propagated from a component to
its HTML elements? To answer this, we must understand the relationship
between a component and an HTML element.
Both components and HTML elements (static and dynamic) share a common
ancestor, WOElement (in Java, Element). WOElement declares, but does not
implement, the three request-handling messages:
invokeActionForRequest:inContext:, and appendToResponse:inContext:. This common
inheritance, of course, makes it possible for both components and HTML
elements to participate in request handling. But there the inherited similarities
end. Although components can generate HTML content, this capability is not
an essential characteristic, as it is with objects on the other branch of the
inheritance tree.
sleep method.
dealloc method is
dealloc method, the object releases any retained
dealloc method in any objects you write. In
takeValuesFromRequest:inContext:,
Component Templates
The first step to generating a component’s HTML page is to create a template
for the component. This template is not the same as the HTML template
discussed in the chapter “What Is a WebObjects Application?” (page 17). In this
context, a template is a graph of WOElement and WOComponent objects
created by parsing and integrating the component’s
25). The network of references corresponds to locations on the page and to
parent-child relationships; for instance, a WOForm element would probably
have WOTextField and WOSubmitButton children.
82
.html and .wod files (see Figure
How HTML Pages Are Generated
page
(component)
page template
Figure 25. An Object Graph for a Page’s Template
The template is created at runtime when the component is first requested.
The template is part of a larger component definition, which also includes
information that allows instances of this component to share resources.
Instances carry only the instance-variable values that are distinctive to
them; the rest is stored in the component definition. You can, if you wish,
enable caching of component definitions so that the component is parsed
only once during an application’s lifetime. To do so, send the application
object a
setCachingEnabled: message in its initialization method.
For each request-handling message, WOComponent’s default behavior is
to forward the message to the objects in its template. To do so, it first
retrieves the template from the component definition. The component
definition returns the WOElement object at the root of the object graph.
This root object, in turn, forwards the message to each of its child elements;
if they have any children, these elements send the message to them. Thus,
each element has, if appropriate, an opportunity to extract user data from
the request, to invoke an action in the component, and to append its
HTML representation to the response.
Each HTML element on a template has an element ID to identify it within
the object graph. An element ID is implemented as an extension of the
sender ID in the URL. You can request the current element ID from the
WOContext object.
83
Chapter 5WebObjects Viewed Through Its Classes
Associations and the Current Component
A dynamic HTML element, such as a text field or a pop-up button, differs from
a static HTML element, such as a heading, in that its attributes can change over
a cycle of the request-response loop. These attributes can include values that
determine behavior or appearance (a “disabled” attribute, for instance), values
that users enter into a field, values that are returned from a method, and actions
to invoke when users click or otherwise activate the element. Each dynamic
element stores its attributes as instance variables of type WOAssociation (in
Java, Association). WOAssociation objects know how to obtain and set the value
they represent. They generally do this using key-value coding.
The key to a value can be represented as a sequence of keys separated by
periods. The resolution of a key by yielding its value makes possible the
resolution of the next key. For instance:
self.aRepetition.list.item
means that self (identifying the current component) has a WORepetition named
aRepetition. The list key denotes the list of elements displayed by the
WORepetition, and the
(including actions) are WOAssociations defined for each dynamic element. The
values for these keys are constants assigned in the
bindings to variables, to methods, or to entities retrieved through a
WODisplayGroup (for applications that access a database).
item is the key to the current item in that list. Keys
.wod file, or they derive from
WOAssociation objects refer to the current component for the initial value of this
sequence. They get this object from the cycle’s WOContext object. Often the
current component is the request or response page of the cycle, but it can be a
reusable component embedded in a page, or even a component incorporated by
one of those subcomponents. See “Subcomponents and Component
References” (page 86) for more on this. WOContext stores the current
component on a stack, “pushing” and “popping” components onto and off of
the stack as necessary.
Depending on the phase of the request-response loop, a dynamic element uses
its WOAssociations to “pull” values from the request (that is, set its values to
what the user specifies) or to “push” its values onto the response page. When a
dynamic element that can respond to user actions (such as WOSubmitButton)
requests the value of its “action” WOAssociation, the appropriate action method
in the current component is invoked and the response page is returned.
The exchange of data through an association that binds an attribute of a parent
component to an attribute of a child component is two-way. This two-way
binding allows the synchronization of state between the two components.
Consider this declaration in
84
Main.wod of the TimeOff example:
How HTML Pages Are Generated
START:Calendar {
selectedDate = startDate;
callBack = "mainPage";
};
In this example, Main is the parent component and Calendar is the child
component. The
selectedDate is a variable of the child component. A change in the parent
startDate variable belongs to the parent component while
component instance variable is automatically communicated through the
association to the child variable. Conversely, a change in value in the child
component variable is communicated to the parent variable. Component
synchronization occurs at the beginning and end of each of the three
request-handling phases of a component (
invokeActionForRequest:inContext:, and appendToResponse:inContext:). Synchronization is
takeValuesFromRequest:inContext:,
performed through the accessor methods of both components.
This aspect of synchronization has implications for developers. Because
WebObjects invokes explicitly implemented accessor methods many times
during the same request-response loop, your accessor methods must have
no side effects. Instead, they should simply set a variable’s value or return a
value. And if they return a value, there should be some way for WebObjects
to set the value.
This rule applies also when the binding involves a parent or a child
component’s method instead of an instance variable. To illustrate this,
assume that
startDate is a method of the Main component instead of an
instance variable. Even in this case, WebObjects attempts to synchronize
startDate with the selectedDate value. In other words, WebObjects attempts to
invoke a
setStartDate: method and raises an exception if such a method does
not exist.
See the chapter “Creating Reusable Components” (page 91) for more on
state synchronization between child and parent components.
Associations and Client-Side Java Components
Client-side Java components, like server-side dynamic elements, use
associations to synchronize state with the parent component. However, the
association class they use is not the same. Instead of using
(the WOAssociation equivalent in Java), they use
next.wo.client.Association, and
this Association class is downloaded to the client along with the component
itself.
Keys for a client-side component fall into two groups: state bindings and
action bindings. State bindings form the basis for state synchronization by
associating state in the applets with state in the server. Action bindings
next.wo.Association
85
Chapter 5WebObjects Viewed Through Its Classes
associate particular events in the client applet (such as clicking a button) with
the invocation of methods in the server.
State is synchronized between the client and the server in three phases:
1. When a page is first generated, the server sends the client all state for which
there are bindings.
2. Before an action is invoked in the server, the client sends the server any of
its state that has changed.
3. After the action is completed, the server sends the client any of its state that
has changed.
This last synchronization occurs only if no new page is returned to the browser.
When a method invoked remotely through an applet action binding returns
it signals that, instead of returning a new page, the server should resynchronize
its state with the applets on the page. WebObjects takes a snapshot of the
changes in state in the server so that only the state that has changed is sent back
to the client.
Note: The last two phases of the synchronization cycle can be initiated only on the
browser side. That is, except for the first “initialization” phase, the server
component can react only to an action triggered in an applet. The component
cannot unilaterally update the state of an applet when its own state changes.
null,
Subcomponents and Component References
A “node” in a template’s object graph can represent a subcomponent (also called
a reusable component) as well as a dynamic or static HTML element. A
dynamic element called a component reference represents all occurrences of the
subcomponent in the parent component. At runtime, the component reference
binds itself to the separate instances. Figure 26 is an example of an object graph
for a page with a subcomponent.
A subcomponent can fire actions against its parent component (using
performParentAction:), and if the parent’s state changes, its state is synchronized
accordingly. In other words, its state is updated to reflect changes according to
its bindings with the parent.
An element ID is assigned to each instance of a subcomponent. When the chain
of request-handling messages traverses an object graph and reaches the
component reference, it resolves references to its instances according to the
element ID of each instance. Components keep track of all their
subcomponents by storing them in an internal dictionary using element IDs as
keys.
86
How HTML Pages Are Generated
page template
page
component
calendar
component
reference
calendar
instance
= page
component
= subcomponent
= static element
= dynamic element
Figure 26. An Object Graph for a Page With a Subcomponent
calendar
template
calendar
instance
87
SPECIAL TASKS
Chapter 6
Creating Reusable Components
In the simplest applications, each component corresponds to an HTML
page, and no two applications share components. However, one of the
strengths of the WebObjects architecture is its support of reusable
components: components that, once defined, can be used within multiple
applications, multiple pages of the same application, or even multiple
sections of the same page.
This chapter describes reusable components and shows you how to take
advantage of them in your applications. It begins by illustrating the benefits
of reusable components. It then describes how to design components for
reuse, how reusable components can communicate with the parent
component, and how state is synchronized between parent and child
components. Finally, it provides some design tips for you to consider when
designing your own reusable components.
Benefits of Reusable Components
Reusable components benefit you in two fundamental ways:
•They help you centralize application resources.
•They simplify interfaces to packages of complex, possibly
parameterized, logic and display.
The following sections explain these concepts in detail.
Centralizing Application Resources
One of the challenges of maintaining a web-based application is the sheer
number of pages that must be created and maintained. Even a modest
application can contain scores of HTML pages. Although some pages must
be crafted individually for each application, many (for example, a page that
gathers customer information) could be identical across applications. Even
pages that aren’t identical across applications can share at least some
portions (header, footer, navigation bars, and so on) with pages in other
applications. With reusable components, you can factor out a portion of a
page (or a complete page) that’s used throughout one or more applications,
define it once, and then use it wherever you want, simply by referring to it
by name. This is a simple but powerful concept, as the following example
illustrates.
Suppose you want to display a navigational control like the one shown in
Figure 27 at the bottom of each page of your application.
93
Chapter 6Creating Reusable Components
Figure 27. A Navigational Control
The HTML code for one page might look like this:
<HTML>
<HEAD>
<TITLE>World Wide Web Wisdom, Inc.</TITLE>
</HEAD>
<BODY>
Please come visit us again!
<!-- start of navigation control -->
<CENTER>
<TABLE BORDER = 7 CELLPADDING = 0 CELLSPACING = 5>
</TR>
</TABLE>
</CENTER>
<!-- end of navigation control -->
</BODY>
</HTML>
Thirteen lines of HTML code define the HTML table that constitutes the
navigational control. You could copy these lines into each of the application’s
pages or use a graphical HTML editor to assemble the table wherever you need
one. But as application size increases, these approaches becomes less practical.
And obviously, when a decision is made to replace the navigational table with an
active image, you must update this code in each page. Duplicating HTML code
across pages is a recipe for irritation and long hours of tedium.
With a reusable component, you could define the same page like this:
94
Benefits of Reusable Components
<HTML>
<HEAD>
<TITLE>World Wide Web Wisdom, Inc.</TITLE>
</HEAD>
<BODY>
Please come visit us again!
<!-- start of navigation control -->
<WEBOBJECT NAME="NAVCONTROL"></WEBOBJECT>
<!-- end of navigation control -->
</BODY>
</HTML>
The thirteen lines are reduced to one, which positions the WebObject
named NAVCONTROL. The declarations file for this page binds the
WebObject named NAVCONTROL to the component named
NavigationControl:
NAVCONTROL: NavigationControl {};
All of the application’s pages would have entries identical to these in their
template and declarations files.
NavigationControl is a component that’s defined once, for the use of all of
the application’s pages. Its definition is found in the directory
NavigationControl.wo in the file NavigationControl.html and contains the HTML for the
Since NavigationControl defines a group of static elements, no declaration
or code file is needed. However, a reusable component could just as well be
associated with complex, dynamically determined behavior, as defined in
an associated code file.
Now, to change the navigational control on all of the pages in this
application, you simply change the NavigationControl component. What’s
more, since reusable components can be shared by multiple applications,
95
Chapter 6Creating Reusable Components
the World Wide Web Wisdom company could change the look of the
navigational controls in all of its applications by changing this one component.
If your application’s pages are highly structured, reusable components could be
the prevailing feature of each page:
Notice that some of these components above take arguments—that is, they are
parameterized. For example, the ProductTable component’s
is set to a particular product identifier, presumably to display a description of
that particular product. The combination of reusability and customizability is
particularly powerful, as you’ll see in the next section.
productCode attribute
Simplifying Interfaces
Another benefit of reusable components is that they let you work at a higher
level of abstraction than would be possible by working directly with HTML
code or with WebObjects’ dynamic elements. You (or someone else) can create
a component that encapsulates a solution to a possibly complicated
programming problem, and then reuse that solution again and again without
having to be concerned with the details of its implementation. Examples of this
kind of component include:
•A menu that posts different actions depending on the user’s choice
•A calendar that lets a user specify start and end dates
•A table view that displays records returned by a database query
To illustrate this feature, consider a simple reusable component, an alert panel
like the one shown in Figure 28.
96
Benefits of Reusable Components
Figure 28. An Alert Panel
This panel is similar to the navigation table shown in Figure 27, but as you’ll
see, most of the component’s attributes are customizable.
To use this component, you simply declare its position within the HTML
page and give it a name:
<HTML>
<HEAD>
<TITLE>Alert</TITLE>
</HEAD>
<BODY>
<WEBOBJECT NAME = "ALERT"></WEBOBJECT>
</BODY>
</HTML>
The declarations file specifies the value for each of the panel’s attributes,
either by assigning a constant value or by binding the attributes value to a
variable in the component’s code (as with the
The component’s code defines the alertTitle and alertDescription instance
variables or methods, which set the text that’s displayed in the upper and
lower panes of the alert panel. The
alertDescription method could, for example,
consult a database to determine the release date of the video.
WebObjects Builder makes working with reusable components such as
AlertPanel even easier. Component clients can simply drag the alert panel
into their components and use the Inspector to set the bindings. They don’t
need to manually edit the declarations file to set these bindings. To set this
up, you, as the component creator, edit a file named
AlertPanel.api specifying
both required and optional attributes. You could, for example, export only
97
Chapter 6Creating Reusable Components
the alertTitle and infoString attributes (leaving the other attributes private) using this
See the WebObjects Tools and Techniques online book for more information.
AlertPanel is one of several components included in a sample application called
ReusableComponents. This application demonstrates and documents how to
create and use reusable components. If you look at the source code for
AlertPanel, you’ll notice that it’s moderately complicated and, in fact, relies on
other reusable components for its implementation. However, WebObjects lets
you think of the AlertPanel component as a black box. You simply position the
component in your HTML template, specify its attributes in the declarations
file, and implement any associated dynamic behavior in your code.
Intercomponent Communication
Reusable components can vary widely in scope, from as extensive as an entire
HTML page to as limited as a single character or graphic in a page. They can
even serve as building blocks for other reusable components. When a reusable
component is nested within another component, be it a page or something
smaller, the containing component is known as the parent component, and the
contained component is known as the child component. This section examines
the interaction between parent and child components.
In the AlertPanel example shown in Figure 28, you saw how the parent
component, in its declarations file, sets the attributes of the child component:
Each of the AlertPanel component’s attributes is set either statically (to a
constant value) or dynamically (by binding the attribute’s value to a variable or
method invocation in the parent’s code). Communication from the parent to the
child is quite straightforward.
98
Intercomponent Communication
For reusable components to be truly versatile, there must also be a
mechanism for the child component to interact with the parent, either by
setting the parent’s variables or invoking its methods, or both. This
mechanism must be flexible enough that a given child component can be
reused by various parent components without having to be modified in any
way. WebObjects provides just such a mechanism, as illustrated by the
following example.
Consider an AlertPanel component like the one described above, but with
the added ability to accept user input and relay that input to a parent
component. The panel might look like the one in Figure 29.
Figure 29. An Alert Panel That Allows User Input
As in the earlier example, you use this component by simply declaring its
position within the HTML page:
Parent's Template File
<HTML>
<HEAD>
<TITLE>Alert</TITLE>
</HEAD>
<BODY>
<WEBOBJECT NAME = "ALERT"></WEBOBJECT>
</BODY>
</HTML>
The corresponding declarations file reveals two new attributes (indicated in
bold):
The parentAction attribute identifies a callback method, one that the child
component invokes in the parent when the user clicks the Yes or No link. The
exitStatus attribute identifies a variable that the parent can check to discover which
of the two links was clicked. This attribute passes state information from the
child to the parent. A reusable component can have any number of callback and
state attributes, and they can have any name you choose.
Now let’s look at the revised child component. The template file for the
AlertPanel component has to declare the positions of the added Yes and No
hyperlinks. (Only excerpts of the implementation files are shown here.)
Child's Template File (excerpt)
<TD>
<WEBOBJECT name=NOCHOICE></WEBOBJECT>
</TD>
<TD>
<WEBOBJECT name=YESCHOICE></WEBOBJECT>
</TD>
The corresponding declarations file binds these declarations to scripted
methods:
Child's Declarations File (excerpt)
NOCHOICE: WOHyperlink {
action = rejectChoice;
string = "No";
};
100
YESCHOICE: WOHyperlink {
action = acceptChoice;
string = "Yes";
};
And the script file contains the implementations of the rejectChoice and acceptChoice
methods:
Child's Script File (excerpt)
id exitStatus;
id parentAction;
- rejectChoice {
exitStatus = NO;
return [self performParentAction:parentAction];
}
- acceptChoice {
exitStatus = YES;
return [self performParentAction:parentAction];
}
Note that exitStatus and parentAction are simply component variables. Depending on
the method invoked,
exitStatus can have the values YES or NO. The parentAction
variable stores the name of the method in the parent component that will be
Loading...
+ hidden pages
You need points to download manuals.
1 point = 1 manual.
You can buy points or you can get point for every manual you upload.