TweetFollow Us on Twitter

Sep 01 Adv WebObjects

Volume Number: 17 (2001)
Issue Number: 09
Column Tag: Advvanced WebObjects 5

Deep into the request/Response Loop

by Emmanuel Proulx

Customizing Web Component Processing

Preface

This new column covers various advanced topics of programming Web applications with WebObjects 5. It is targeted towards knowledgeable WebObjects developers who are looking for that extra wisdom to help them go further.

In this first article, I introduce the Request/Reply loop, and how to customize it and control how Components are processed. I hope this will be valuable to you.

Overview

From the time a user clicks on a hyperlink to the time the page is displayed in the browser, lots of things happen. Of course, the browser and the Web server first initiate the communication and ask for a page. If you installed WebObjects with the CGI adaptor (as opposed to a native one), the following type of URL is used:

http://server.domain.subdomain/cgibin/WebObjects/ApplicationName

Here the WebObjects adaptor program is being called, and if there is an application called "ApplicationName" registered, it is being executed. WebObjects builds a page and returns it, calling your code to fill in the blanks. This part is very transparent. But the building of a page is not. How does it work? What happens? When does it happen?

Knowing the answer to these questions is important to expand, tweak and adapt your WebObjects program to the specific requirements of your target system. Figure 1 illustrates how it works:


Figure 1. Objects of the Request/Response Loop.

The WebObjects adaptor program builds a Request object, which encapsulates the original "Get/Post" message received by the Web Server. This object is passed around the framework as WebObjects works on the Component (page template). Then WebObjects builds the Response object, which encapsulates the returned (pure HTML) page.

During this time, many classes of the framework call each other. You know about the Application and Session classes. You just learned about the Request, Response and Component classes. When they interact, they call different functions at different times.

Knowing when the functions are called and overriding the correct function is the key to customizing the request/response loop. Figure 2 illustrates the different objects and methods and their interactions. The next sections describe them further, and prescribe when to overload them.


Figure 2. Interactions Between Methods.

Application Object

The Application class is a subclass of the virtual WOApplication class. Your Application class will only have a single instance per server. This class contains the main() function. By default, the generated code for this function calls WOApplication.main(), which creates a Singleton instance of your Application class. You have to use the following definition so your subclass will be recognized:

public class Application extends WOApplication

I have also stated that the Application object is accessible from all Sessions on the server, and its main use is to share data among them, and to hold global business logic. The Application's underling goal is to manage the Sessions and the request/response loop. Typically, you overload at the Application level if your code is general to all clients. That said, here are the main functions that you can overload to customize the framework to your needs:

  • main(String []) : Overload to initialize variables before the system is started. Note that the default behavior is to call WOApplication.main(), which creates an instance of Application.
  • Application() (Constructor): Ideal to put the system-wide initialization code.
  • handleRequest(WORequest): Takes care of a single iteration of the request/response loop. The base implementation calls awake(), takeValuesFromRequest(), invokeAction(), appendToResponse() and finally sleep().
  • awake():Called by the framework when there's a new request, before WebObjects begins processing it. Overload for example when you want to keep statistics of the frequency at which the system is being used.
  • takeValuesFromRequest(WORequest, WOContext): The base implementation calls Session.takeValuesFromRequest(). As its name stipulates, this function should be overridden when you want to process the request object from the application object.
  • WOResponse infokeAction(WORequest, WOContext): The base implementation executes the action associated to a Request, by calling Session.invokeAction(). This function should be overridden if you intend to act upon the action itself (for example, to stop one from being executed).
  • appendToResponse(WOResponse, WOContext): The base implementation calls Session.appendToResponse(). As its name says, this function should be overridden to add to the Response, or for any post-action operation.
  • sleep(): Called by the framework after a request/response is done with, before WebObjects waits for the next one. Overload for example when you want to keep statistics of the requests duration.
  • finalize(): The Application's destructor function is ideal to put the system-wide cleanup code.

Session Object

The Session object is a subclass of the virtual WOSession class. Your Session class has one instance per HTTP connection. WebObjects creates all instances for you, but you have to use the following definition:

public class Session extends WOSession

The main use of the Session class is to hold connection-specific information and business logic. Under the hood, it's also in charge of terminating itself and holding the Editing Context (database objects). You will notice that most of the functions you can overload are similar to the Application's. This is so you can decide the scope of your overloaded code. Typically, you overload at the Session level if the code is connection- or client-specific. The interesting functions are:

  • Session() (Constructor): The constructor is called a single time shortly after WOApplication.awake() is called, but won't be called again for the current client. This function is ideal to put the connection-specific initialization code.
  • awake(): Called by the framework when there's a new request, during WOApplication.awake(), before WebObjects begins processing the request. The base implementation calls WOComponent.awake(). Overload to call request-specific initialization code.
  • takeValuesFromRequest(WORequest, WOContext): Called during Application.takeValuesFromRequest(). The base implementation calls takeValuesFromRequest().in the page that was requested. This function should be overridden when you want to process the Request object from the Session object.
  • WOResponse infokeAction(WORequest, WOContext): Called during Application.invokeAction(). The base implementation executes the action associated to a Request by calling invokeAction() in the page that was requested. This function should be overridden if you intend to act upon the action itself (for example, to stop one from being executed), depending on a client-specific state.
  • appendToResponse(WOResponse, WOContext): Called during WOApplication.appendToResponse(). The base implementation calls appendToResponse() in the page that was requested. This function should be overridden to add to the Response, or for any post-action operation.
  • sleep(): Called by WOApplication.sleep() after a request/response is done with, before WebObjects waits for the next one.
  • finalize(): The Session's destructor function is ideal to put client-specific cleanup code.

Web Component

The Component objects are subclasses of the virtual WOComponent class. There can be multiple instances of a Component, and they are usually represented by local references.

WebObjects creates an instance of the Component with the name "Main" for you, returning it to the client browser, but you have to use the following definition:

public class Main extends WOComponent

You are responsible for creating instances of other Components. Usually, a Component is returned when an action is invoked. In that case, you create an instance of a Component by using this syntax:

WOComponent anAction() {
  return pageWithName("ComponentName");
}

The main use of the WOComponent subclass is to hold page-specific data and logic. On top of that, Components have the ability to generate the result page (using method generateResponse()). Again, most of the functions you can overload are similar to the Application's and the Session's. Typically, you overload at the Component level if the code is page-specific. The functions you can overload are:

  • Web Component Constructor: The constructor is called when pageWithName() is called. This function is ideal to put the page-specific one-time initialization code. Use awake() to put page-specific per-request initialization code.
  • awake(): Called by the framework when there's a new request, during WOSession.awake(), before WebObjects begins processing the request. The base implementation does nothing. Overload to call page-specific per-request initialization code.
  • takeValuesFromRequest(WORequest, WOContext): Called during WOSession.takeValuesFromRequest(). The base implementation calls WOElement.takeValuesFromRequest(), where WOElement is the root element () of the hierarchy of the page. The function is then called recursively on each element of the hierarchy. This function should be overridden when you want to process the Request object from the current page.
  • WOResponse infokeAction(WORequest, WOContext): Called during WOSession.invokeAction(). The base implementation executes the action associated to a Request directly. This function should be overridden if you intend to act upon the action itself (for example, to stop one from being executed), depending on a page-specific state.
  • appendToResponse(WOResponse, WOContext): Called during WOSession.appendToResponse(). The base implementation calls WOElement. appendToResponse(), where WOElement is the root element () of the hierarchy of the page. The function is then called recursively on each element of the hierarchy. This function should be overridden to add to the Response, or for any post-action operation.
  • sleep(): Called by WOSession.sleep() after a request/response is done with, before WebObjects waits for the next one. Ideal for per-request page-specific cleanup code.
  • finalize(): The Component's destructor function is ideal to put one-time page-specific cleanup code. Use sleep() to call per-request page-specific cleanup code.

Element Object

There's an object that I skipped on purpose here, the WOElement object. You usually don't subclass this class, unless you want to write your own customized elements. You can avoid this trouble by using custom components instead, which is easier to write. The interesting member functions are the constructor, takeValuesFromRequest(), invokeAction(), appendToResponse() and finalize(). The usage of these functions should be trivial.

Request Object

As stated before, the Request class encapsulates a Get or a Post HTTP request. The base class for WORequest is WOMessage, so some of the characteristics explained here are inherited from that base class.

The framework uses an instance of WORequest to pass it around during the pre-generation phase (awake(), takeValuesFromRequest() and invokeAction() functions). Here's a partial list of the information held in this class:

  • A list of "cookies" (cookies are key/value pairs stored in the client's Browser). Keys are user-defined only. Cookies will be discussed in their own section.
  • A list of all submitted form fields when applicable (Post request only). Form fields will be discussed later on.
  • A list of "headers" (headers are key/value pairs containing the context of the request).
  • The bits that make up the requested URL.
  • Other request information (encoding, languages, protocol, request type).

I will skip cookies and form fields; they are covered later on. As for headers and the other information, there's no prescribed way of using these. It's up to you to figure out how to use them in your advantage. I can only show you how to access them. Following is an overview of the available functions.

Headers

Accessing headers is done with these functions of the class WORequest:

  • NSArray headerKeys (): Returns a list of all available header names as Strings. Use these as keys in functions headerForKey() and headersForKey().
  • String headerForKey(String) and NSArray headersForKey(String): These functions return the value (or values) of a header, when you pass the header's name (key). Use the first for single-value headers, and the second for multiple values.

But what are the fundamental request headers and what are they used for? Here's a link that lists some of the basic request headers:

http://www.w3.org/Protocols/HTTP/HTRQ_Headers.html

But there may be other headers, because some are specific to the Web browser. To find out about those extra headers, the first thing that comes to mind is to overload takeValuesFromRequest() in one of Application, Session or Main, and print them out.ĘThe following piece of code does exactly that:

public void takeValuesFromRequest(WORequest r, WOContext c) 
{
  super.takeValuesFromRequest(r,c);
Ę
  if(r.headerKeys() == null) return;
  for(int i=0; i< r.headerKeys().count(); i++) {
    String key = (String)r.headerKeys().objectAtIndex(i);
    NSArray values = r.headersForKey(key);
    System.out.println("Found header " + key + 
      ": " + value.toString());
  }
}

Here's what the output might look like:

Found header: accept-charset = ("iso-8859-1,*,utf-8")
Found header: accept-language = (bg)
Found header: accept-encoding = (gzip)
Found header: connection = ("keep-alive")
Found header: user-agent = ("Mozilla/4.6 [en] (WinNT; I)")
Found header: host = ("localhost:3878")
Found header: accept = ("image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*")
Found header: referer = ("http://www.imaginarypenda.com/gotosite.html")

Dismantling URLs

Next, let's have a look at the functions returning the parts of the requested URL. Let's use an example for each function. Imagine the user wrote the address:
http://www.imaginarypenda.com/cgi-bin/WebObjects/App

Here are the methods of WORequest to take a URL apart:

  • String uri(): Returns the last part of the whole URL, from the first slash to the end. For example, if the user typed the URL above, this method should return "/cgi-bin/WebObjects/App".
  • String adaptorPrefix(): Provides the adaptor's name. For example it could be "/cgi-bin/WebObjects/" on Mac OS X or Unix, or "/cgi-bin/WebObjects.exe/" on Windows. This example shows the CGI adaptor prefix; it may be different if using a native Web server adaptor.
  • String applicatioNumber(): Returns the user-requested application number, when provided. This information is usually not provided by the user in the URL (as not shown above). In these cases, this functions returns -1 and meaning any one instance of the application was called.
  • String applicationName(): Returns the application's name. In our example, it is "App".

Let's dissect some URLs and see which function return which part. Here are three examples.

  • The first example below shows a MacOS X-based server, pointing to any instance of Find-A-Luv (no instance number). In this case, applicationNumber() will return -1.
  • The second is a Windows-based server, pointing to any instance (we could have skipped the -1).
  • The third is a Unix-based server, with native adaptor (no CGI involved), and pointing to the third instance.
http://www.aUrl.com   /cgi-bin/   WebObjects/          /   Find-A-Luv
http://www.aUrl.com   /cgi-bin/   WebObjects.exe/        -1   /   Find-A-Luv
http://www.aUrl.com   /WebObjects/      3   Ę   Find-A-Luv
Ę   adaptorPrefix()      applicationNumber()      applicationName()
Ę   
Ę   uri()

Other Request Info

Following is an brief overview the "other information" functions:

  • method() String Any valid HTTP method, like "GET", "PUT, "POST", "HEAD", etc. See http://www.w3.org/Protocols/HTTP/ Methods.html for a complete list of methods and their usage.
  • browserLanguages() NSArray of String The list of languages (see your browser's language preferences).
  • content() NSData Always null unless there was raw data posted with the request.
  • httpVersion() String For example, "HTTP/1.0".
  • userInfo() NSDictionary A set of key/value pairs, passed around during the processing of the request. Empty by default, but you can use it to convey data around the framework.

I don't want to spend too much space on these functions since their usefulness is limited.

Response Object

The Request's counterpart is the Response object, which encapsulates the page returned to the requesting browser. The WOResponse class is also a subclass of WOMessage, so some of the characteristics we'll cover come from that base class.

Typically, the Response object is created by the framework and passed around the elements hierarchy during the generation phase. Then it is passed to the Component, Session and Application objects, during the post-generation phase. All of this is accomplished by function appendToResponse().

The information in this object is very similar to the Request object. The difference is that you can change this information, and write to the buffer holding the returned page. Here's an overview of the kind of information you can get and set:

  • A list of cookies. Cookies will be discussed in their own section.
  • A list of resulting headers.
  • The actual contents of the returned page.
  • Other request information (status, encoding, protocol).

Response Headers

We saw how to get headers in previous sections. The same getter functions exist in the WOResponse object (headerKeys(), headerForKey(), headersForKey()). On top of that, you can set the headers returned to the browser:

setHeader(String value, String key) and setHeaders(NSArray values, String key): These functions fix the value (or values) of a header, given its name (key). Use the first for single-value headers; use the second for multiple values.

A list of basic response headers can be seen at:Ę
http://www.w3.org/Protocols/HTTP/Object_Headers.html

Response Text

For getting and setting the contents of the returned page, WOResponse has many methods. Here are a few of them:

  • NSData contents(): The raw bytes that constitute the returned HTML. An NSData object encapsulates an array of bytes (byte []), and the interesting functions are bytes() and length().
  • setContent(NSData): Replaces the whole returned page with the one you provide.
  • appendContentString(String): Adds the specified string to the end of the resulting page without changing anything.
  • appendContentHTMLString(String): Adds the specified string to the end of the page, but escapes the HTML-specific characters. For example, the character '<' will be changed to '&lt'.

WARNING: you usually don't want to use setContent() because it wipes out the previously computed HTML. Use the append...() methods instead.

Other Response Info

The "other information" methods of WOResponse include:

  • defaultEncoding()
    setDefaultEncoding(int)
    Gets and sets the default encoding, specifying the character set that should be used by the returned contents.
  • contentEncoding()
    setContentEncoding(int)
    Gets and sets the encoding, specifying the character set that is being used by the returned contents.
  • httpVersion() Gets and sets the a string indicating the protocol format
  • status()
    setStatus(int)
    Gets and sets the status, indicating if the generation was successful (status() returns 200) or if an error occurred. See this page for a list of status codes: http://www.w3.org/Protocols/HTTP/HTRESP.html
  • userInfo()
    setUserInfo(NSDictionary)
    A set of key/value pairs, passed around during the processing of the response. Empty by default, but you can use it to convey data around the framework.

Conclusion

As you've seen here, there is more to a request/response loop than simply asking for a page and getting the HTML back. Many objects and methods are called along the way. As we've seen, most of the methods can be overloaded to intervene in the loop at a specific moment. We've also looked at the ways to manipulate the request and response themselves. Again, what to do with this knowledge is up to you, but I can almost hear your brain pondering.


Emmanuel Proulx is a Course Writer, Author and Web Developer, working in the domain of Java Application Servers. He can be reached at emmanuelp@theglobe.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Six fantastic ways to spend National Vid...
As if anyone needed an excuse to play games today, I am about to give you one: it is National Video Games Day. A day for us to play games, like we no doubt do every day. Let’s not look a gift horse in the mouth. Instead, feast your eyes on this... | Read more »
Old School RuneScape players turn out in...
The sheer leap in technological advancements in our lifetime has been mind-blowing. We went from Commodore 64s to VR glasses in what feels like a heartbeat, but more importantly, the internet. It can be a dark mess, but it also brought hundreds of... | Read more »
Today's Best Mobile Game Discounts...
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Nintendo and The Pokémon Company's...
Unless you have been living under a rock, you know that Nintendo has been locked in an epic battle with Pocketpair, creator of the obvious PokĂ©mon rip-off Palworld. Nintendo often resorts to legal retaliation at the drop of a hat, but it seems this... | Read more »
Apple exclusive mobile games don’t make...
If you are a gamer on phones, no doubt you have been as distressed as I am on one huge sticking point: exclusivity. For years, Xbox and PlayStation have done battle, and before this was the Sega Genesis and the Nintendo NES. On console, it makes... | Read more »
Regionally exclusive events make no sens...
Last week, over on our sister site AppSpy, I babbled excitedly about the PokĂ©mon GO Safari Days event. You can get nine Eevees with an explorer hat per day. Or, can you? Specifically, you, reader. Do you have the time or funds to possibly fly for... | Read more »
As Jon Bellamy defends his choice to can...
Back in March, Jagex announced the appointment of a new CEO, Jon Bellamy. Mr Bellamy then decided to almost immediately paint a huge target on his back by cancelling the Runescapes Pride event. This led to widespread condemnation about his perceived... | Read more »
Marvel Contest of Champions adds two mor...
When I saw the latest two Marvel Contest of Champions characters, I scoffed. Mr Knight and Silver Samurai, thought I, they are running out of good choices. Then I realised no, I was being far too cynical. This is one of the things that games do best... | Read more »
Grass is green, and water is wet: Pokémo...
It must be a day that ends in Y, because PokĂ©mon Trading Card Game Pocket has kicked off its Zoroark Drop Event. Here you can get a promo version of another card, and look forward to the next Wonder Pick Event and the next Mass Outbreak that will be... | Read more »
Enter the Gungeon review
It took me a minute to get around to reviewing this game for a couple of very good reasons. The first is that Enter the Gungeon's style of roguelike bullet-hell action is teetering on the edge of being straight-up malicious, which made getting... | Read more »

Price Scanner via MacPrices.net

Take $150 off every Apple 11-inch M3 iPad Air
Amazon is offering a $150 discount on 11-inch M3 WiFi iPad Airs right now. Shipping is free: – 11″ 128GB M3 WiFi iPad Air: $449, $150 off – 11″ 256GB M3 WiFi iPad Air: $549, $150 off – 11″ 512GB M3... Read more
Apple iPad minis back on sale for $100 off MS...
Amazon is offering $100 discounts (up to 20% off) on Apple’s newest 2024 WiFi iPad minis, each with free shipping. These are the lowest prices available for new minis among the Apple retailers we... Read more
Apple’s 16-inch M4 Max MacBook Pros are on sa...
Amazon has 16-inch M4 Max MacBook Pros (Silver and Black colors) on sale for up to $410 off Apple’s MSRP right now. Shipping is free. Be sure to select Amazon as the seller, rather than a third-party... Read more
Red Pocket Mobile is offering a $150 rebate o...
Red Pocket Mobile has new Apple iPhone 17’s on sale for $150 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Switch to Verizon, and get any iPhone 16 for...
With yesterday’s introduction of the new iPhone 17 models, Verizon responded by running “on us” promos across much of the iPhone 16 lineup: iPhone 16 and 16 Plus show as $0/mo for 36 months with bill... Read more
Here is a summary of the new features in Appl...
Apple’s September 2025 event introduced major updates across its most popular product lines, focusing on health, performance, and design breakthroughs. The AirPods Pro 3 now feature best-in-class... Read more
Apple’s Smartphone Lineup Could Use A Touch o...
COMMENTARY – Whatever happened to the old adage, “less is more”? Apple’s smartphone lineup. — which is due for its annual refresh either this month or next (possibly at an Apple Event on September 9... Read more
Take $50 off every 11th-generation A16 WiFi i...
Amazon has Apple’s 11th-generation A16 WiFi iPads in stock on sale for $50 off MSRP right now. Shipping is free: – 11″ 11th-generation 128GB WiFi iPads: $299 $50 off MSRP – 11″ 11th-generation 256GB... Read more
Sunday Sale: 14-inch M4 MacBook Pros for up t...
Don’t pay full price! Amazon has Apple’s 14-inch M4 MacBook Pros (Silver and Black colors) on sale for up to $220 off MSRP right now. Shipping is free. Be sure to select Amazon as the seller, rather... Read more
Mac mini with M4 Pro CPU back on sale for $12...
B&H Photo has Apple’s Mac mini with the M4 Pro CPU back on sale for $1259, $140 off MSRP. B&H offers free 1-2 day shipping to most US addresses: – Mac mini M4 Pro CPU (24GB/512GB): $1259, $... Read more

Jobs Board

All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.