Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Ajax Patterns And Best Practices (2006)

.pdf
Скачиваний:
39
Добавлен:
17.08.2013
Размер:
15.94 Mб
Скачать

C H A P T E R 7 R E P R E S E N T A T I O N M O R P H I N G P A T T E R N

219

the representation is stored as XML-compliant HTML. By using an XSLT transformation, the state is extracted from one representation and injected into another.

XSLT requires some content to transform. In the case of the Representation Morphing pattern, the content to transform is the other representation. The result is another transformation. What this means is that all of the representations need to use some common HTML tags such as HTML form elements, or span tags, or some other common tag. The common tags can be surrounded with unknown other tags because they are considered irrelevant. In a nutshell, the XSLT transformation accomplishes the same as the parseElement and processElement functions. The difference with the XSLT transformation is that it is accomplished all in one step, and not in two function calls. Furthermore, the entire processing is XML-based.

When using XSLT, the representation is not a classical Model View Controller architecture because the controller and the view are combined. Let’s look back at the HTML framework and at how the XSLT reference point is defined:

<div id="htmlxslt">

<div id="xsltFromSpan" style="visibility:hidden"> <![CDATA[ ]]>

</div>

<script language="JavaScript"></script> <div id="htmlxsltdest">

</div>

</div>

An XSLT reference point has at least three child elements. The first child element, div, with the identifier xsltFromSpan, contains the XSLT transformation that will be used to transform the HTML. Notice that embedded within the child div element is a CDATA section. The CDATA section is required because otherwise some escaped text will be unescaped. More about this will be illustrated shortly. The second child element, script, is used to execute the XSLT script. And the third child element, a div element, contains the representation generated by the transformation.

The example contains only a single XSLT sheet, representing a single transformation from one representation to another representation. This results in the necessity of having multiple XSLT sheets that represent a transformation from one representation to another. Typically this results in multiple transformations that are similar but not identical. One of the problems of using XSLT is that a certain starting and ending representation are required, which requires more coding and maintenance than using the JavaScript commonality approach.

The XSLT sheet that can be used to transform the span elements to the third representation in Figure 7-13 is as follows:

<div id="xsltTransformUsingSpan" style="visibility:visible"> <![cdata[

<xsl:stylesheet> <xsl:template match="/">

<xsl:apply-templates select="//span"/> </xsl:template>

220

C H A P T E R 7 R E P R E S E N T A T I O N M O R P H I N G P A T T E R N

<xsl:template match="span">

<xsl:if test="@display">

<xsl:value-of select="@display" />

<xsl:text disable-output-escaping="no">

<input type="text" id="</xsl:text>

<xsl:value-of select="@id" />

<xsl:text disable-output-escaping="yes">" value="</xsl:text>

<xsl:value-of select="."/>

<xsl:text disable-output-escaping="yes">" /> <br /></xsl:text>

</xsl:if>

</xsl:template>

</xsl:stylesheet>]]>

</div>

Describing how XSLT works is beyond the scope of this book, but I will explain the important pieces, which have been indicated in bold. The bold XML element xsl:template is a match instruction that will match a span element if it is encountered while iterating the HTML content. All of the other elements are ignored. If a span element is encountered, HTML content is generated by using the xsl:text tags. This is the crux of the problem when using XSLT, as the HTML is embedded within the XSLT document.

Earlier I mentioned that the CDATA section was necessary because of escaped sequences. The xsl:text tags embed the <, ", and < identifiers, which are escape sequences used to generate incomplete XML tags. If the CDATA instruction were not used, the escape characters would be unescaped and the generated representation would be incorrect.

To execute the XSLT stylesheet, the Google XSLT library6 is used. The source code is as follows:

el( "htmlxslt").transfromFromSpan = function( src) { var xml = xmlParse( src);

var xslt = xmlParse(trimBuffer( el('xsltTransformUsingSpan').innerHTML)); var html = xsltProcess(xml, xslt);

el( "htmlxsltdest").innerHTML = unescapeHTML( html);

}

The function xmlParse is used to convert a buffer containing XML into an object structure that can be used by the Google XSLT library. The xmlParse is called twice: once for the content to transform, and once for the XSLT sheet. To execute the XSLT, the function xsltProcess is called with the XML and XSLT content, resulting in a buffer of HTML. The HTML buffer is then assigned to the local representation reference node.

Overall, the XSLT solution is relatively straightforward so long you know how to write XSLT programs. The only real problem when using XSLT is the binding of the reading of the representation with the generation of the representation. This requires each representation to know which representation type that it will be reading from.

6. http://sourceforge.net/projects/goog-ajaxslt/

C H A P T E R 7 R E P R E S E N T A T I O N M O R P H I N G P A T T E R N

221

Some Implementation Details

Thus far, the pattern implementation has not illustrated the challenges that you will be confronted with. You might have noticed in previous examples that custom attributes and properties were assigned to the HTML object model. The HTML object model is flexible and allows it, although there are some gotchas. The gotchas are not critical because there are ways around them, but a developer needs to be aware of them.

I will use an abbreviated explanation when demonstrating the gotchas. I will illustrate them by using a single piece of HTML and then Figures 7-15 and 7-16. Finally, I will present a list highlighting each gotcha illustrated in the code and figures.

The code is as follows:

<html>

<title>Hello world</title>

<script language="JavaScript" type="text/javascript"> function el(id) {

return document.getElementById(id);

}

function OnClickMe() {

el( "txtArea1").value = el( "parentArea2").innerHTML;

}

function OnClickForMyProperty() {

el( "txtArea2").value = el("txtArea2").directAssignedString;

el( "txtBoxArea2").value = el( "txtArea2").directAssignedObject.prop;

}

function OnLoad() {

el( "txtArea2").directAssignedString = "direct assigned string"; var obj = new Object();

obj.prop = "property on an object";

el( "txtArea2").directAssignedObject = obj;

el( "txtArea2").setAttribute( "attributeAssignedString", "attributed assigned string");

el( "txtArea2").setAttribute( "attributeAssignedObject", obj);

}

</script>

<body onload="OnLoad()"> <div id="element">

Hello world </div>

<div id="parentArea1">

<textarea id="txtArea1" cols="60" rows="10"> Nothing

</textarea>

</div>

222 C H A P T E R 7 R E P R E S E N T A T I O N M O R P H I N G P A T T E R N

<div id="parentArea2">

<textarea id="txtArea2" cols="60" rows="10"> Nothing

</textarea><br />

<input type="text" id="txtBoxArea2" value="hello" /> </div>

<input type="button" value="Click For My Property First" onclick="OnClickForMyProperty()" />

<input type="button" value="Click Me Second" onclick="OnClickMe()" /> </body>

</html>

Figure 7-15. Pattern specifics page loaded in Mozilla Firefox

C H A P T E R 7 R E P R E S E N T A T I O N M O R P H I N G P A T T E R N

223

Figure 7-16. Pattern specifics page loaded in Microsoft Internet Explorer

Here are some specific implementation points about the HTML content:

Properties assigned by using the notation el( "txtArea2").directAssignedString are not visible when the HTML element is serialized using the innerHtml property.

Properties that are assigned by using the method setAttribute are visible for both browsers when using the innerHTML property.

Properties that are assigned with an object instance by using the method setAttribute are not visible as attributes for all browsers when using the innerHTML property.

Properties (for example, el( "txtArea2").value) that are assigned dynamically are not visible for all browsers when using the innerHTML property.

Modified HTML content that is saved from the browser by using Save As is not stored consistently across all browsers.

224

C H A P T E R 7 R E P R E S E N T A T I O N M O R P H I N G P A T T E R N

What the specifics should illustrate is that the HTML document model is implemented consistently across both browsers. What is implemented inconsistently is whether the changes to the HTML page are visible when using the standard properties (for example, innerHTML). The rule of thumb is to assign properties to individual HTML elements by using the JavaScript property notation (for example, element.property) and not to use the setAttribute function. Then to get a consistent object model, the object model should be iterated by using a function that generates a true object model that innerHTML might not.

Pattern Highlights

The Representation Morphing pattern is used to enable a mechanism whereby content can be viewed, edited, or navigated by using best-of-breed user interfaces. When using the Representation Morphing pattern, state structure is a constant that is transferred from representation to representation.

This chapter focused on the following aspects:

Defining a state in a representation.

Transferring state from one representation to another.

Using a representation as a state is acceptable so long as the state that is embedded is defined by using a clear and consistent manner. What you want to avoid is the defining of state that requires extra deciphering routines, making it more complicated to transfer a state from one representation to another.

Even though the focus has been on transforming editable content into viewable content, there are other situations. The conversion of data from editable to read-only happens to be a clear example of when to use the Representation Morphing pattern.

The JavaScript code associated with a representation is not used to store state but to manipulate state, and therefore must be stateless.

It is possible to use XSLT to transform content from one representation to another, but the generic JavaScript solution is simpler.

C H A P T E R 8

■ ■ ■

Persistent Communications

Pattern

Intent

The Persistent Communications pattern provides a mechanism enabling the client and the server to communicate with each other on a persistent basis so that the client can send messages to the server, and the server can send messages to the client.

Motivation

Client-server programming, which HTTP is, requires that the client call the server to process a request. The server obliges, processes the request, and sends a response. This is a standard run-of-the-mill operation.

Now imagine writing a web application that acts like a bulletin board. Each individual has the ability to create a message and post it. One extension would be offering the capability, when the messages are displayed, to indicate whether the message poster is online. When a reader reads a posting and finds it of interest, the reader might want to ask further questions. If the reader knew that the poster was online, the reader could ask questions immediately. This capability is offered by Yahoo! and is illustrated in Figure 8-1.

In Figure 8-1, there are a number of rows that begin with a number followed by a title. Following the title is the name of the person who wrote the message. Below that name is the nickname of the person, and below that is a circle with either a smiley face or a face with another expression. The smiley face indicates that the person who wrote the message is available for discussion using Yahoo! instant messaging.

Associating the availability of a user with a posted message is a clever combination that integrates two distinctly separate solutions. The posted messages are generated from content in a database. The availability status of a user is from an instant messaging service. What makes the integration possible between the messaging service and the content from a database is the user who posted the message and is using the service.

225

226

C H A P T E R 8 P E R S I S T E N T C O M M U N I C A T I O N S P A T T E R N

Figure 8-1. Yahoo! message board that displays messages and the availability of the message creator

The different services generate very different types of data. When a message is posted, it is read-only. Messages are typically not changed, but they may be deleted. In contrast, the status of the user will change, and more important, a reader does not know when the status will change. Generating content for these two dissimilar streams is complicated because when a message is generated and displayed in an HTML page, it will not change. Yet the status of the user icon can change while the reader is inspecting the postings.

A possible strategy when generating the content is to consider the status of the user as single-shot display-only status. The single-shot display strategy will generate a status as the content is generated, and after that the status is not updated. Most likely, a reader will glance over the messages and decide within a minute or so whether to contact the message poster. If the message poster was available as the content was generated, chances are pretty high that the message poster will be around for a minute or so. The problem with the strategy is that if the

C H A P T E R 8 P E R S I S T E N T C O M M U N I C A T I O N S P A T T E R N

227

HTML page is loaded and the reader does not read the message until 10 minutes later due to a coffee break, the status will be out-of-date.

Another strategy is to poll the server on the status of the message author. When the page is generated, an initial status is defined. Then, after a minute or whatever period decided by the generated script, the status is updated. The problem with polling is that the status is current only when the poll request has been made. Between polls, the state is considered stale and not representative. So to keep a poll representative, you poll more often, but what is a good polling frequency? Let’s say that you poll every second; well, then the waiting time is not significant from a reader perspective. However, for a real-time system that is an eternity. The point is that the poll time needs to be adjusted to the nature of the data and that there is no single best universal poll frequency.

Yet another strategy is for the server to contact the client and inform the client of a change in status. That strategy seems to be the best but is not possible because of the Internet infrastructure. (I will explain the details later, in the “Architecture” section.)

Regardless of the strategy implemented, there is a need for the server to update the client with new information for one reason or another.

Applicability

The main argument for using the Persistent Communications pattern is for the web browser (client) to be able to receive information updates from the server. The server has the ability to communicate to the client, without the server having to wait for the client to ask for an information update.

The pattern has three main implementation types that are direct solutions to the problem that they solve:

Status updates: A status update is a piece of global information that a client is interested in and that is stored on a server. Multiple clients see the same representation of the data. An example is a ticker tape of current stock prices, like the ticker tape on a financial television program. The information is not intended for a specific user, as all users see the same information. To view the information, users do not need to identify themselves. It does not mean that the data is free for all. In other words, a group of authorized people can access the resource, but the resource is not dependent on those accessing the resource.

Presence detection: Presence detection occurs when multiple clients are interested in the same global resource. The global resource has the same representation for all clients, except that the state of the resource is dependent on the clients viewing the resource. Figure 8-1, which shows the status of the message creator, is an example of presence detection. The resource might be updated by an external process but can also be updated by the individual users.

Server push: Server push occurs when multiple clients register with a global resource but are allocated a unique resource. It is the server’s discretion on how the identified user is cross-referenced to the unique resource. An example is a user-defined view of a ticker tape. The stock prices are identical for all users, but which stock prices are shown is unique to each user. The server has to manage which information is pushed to which user. The clients indicate only what they are interested in.

228

C H A P T E R 8 P E R S I S T E N T C O M M U N I C A T I O N S P A T T E R N

The last scenario is explicitly called a server push; however, in theory all three implementation types could be considered a server push because all three involve the server sending data to the client. There are two attributes that separate the various implementation types: what the representation of the resource is, and whether a user has to be identified when accessing

a resource.

Associated Patterns

The Persistent Communications pattern has some overlap with the Decoupled Navigation pattern. Specifically, for the Decoupled Navigation pattern it is possible to trigger an event that would call the same server-side resource as the Persistent Communications pattern. The Persistent Communications pattern differs in that its purpose is focused on the server sending data to the client. The purpose of the Decoupled Navigation pattern is not to push data from the server to the client, but to provide a mechanism for separating navigational functionality.

The Persistent Communications pattern does not implement the Content Chunking pattern, or at least is not required to implement the Content Chunking pattern. More likely, the Persistent Communications pattern will send data that implements the Infinite Data pattern. The Persistent Communications pattern is a very specific pattern for a very specific situation, as outlined in the preceding “Applicability” section. Using it in any other situation complicates the solution. In those cases, it would make more sense to make the Decoupled Navigation pattern behave like the Persistent Communications pattern.

Architecture

Explaining the architecture of the Persistent Communications pattern is relatively simple. The problem, though, is that the explanation might have you scratching your head on why such a solution was created in the first place. The solution might seem inefficient and overly complex, and you might think it could have been solved in another way. It is not possible to solve the server-to-client communications in any other way because of how the Internet architecture is implemented.

Even if there were no implementation problems from the perspective of the Internet, defining and implementing the Persistent Communications pattern would still be necessary because the HTTP protocol was not intended for such functionality. The design of the HTTP protocol allows for only stateless interaction that starts with the client, is processed by the server, and ends with the client. The challenge here is that HTTP is being asked to do what it was never designed to do, and this pattern is presenting some solutions.

Before you learn about the architecture of the Persistent Communications pattern, first you need to understand the problem of the “broken” Internet.

Why the Internet Is “Broken”

It is a bold statement to say that the Internet is “broken.” I don’t mean that it is unfixable or bad, but that the Internet has transformed into an architecture that is not optimal. The “not optimal” part relates to Internet Protocol (IP) addresses. To understand the transformation, let’s go back in time and look at how the Internet worked in the late eighties, as illustrated in Figure 8-2.