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

Ajax Patterns And Best Practices (2006)

.pdf
Скачиваний:
44
Добавлен:
17.08.2013
Размер:
16 Мб
Скачать
☆

C H A P T E R 1 0 ■ I N F I N I T E D A T A P A T T E R N

329

to deadlock at the worst times. Of course, a deadlock could be debugged, and then magically as the debugger is started the deadlock disappears. At that point, your choice is to distribute the application while running the debugger or write code that is logically correct.1

The last part of the task manager are the methods used to manage the results. These methods use synchronization techniques that do not involve the lock keyword. The synchronization mechanism is a Monitor. A Monitor and lock act similarly, but a Monitor has one ability that lock does not: monitors can be signaled. Signaling is the ability of a thread to put itself to sleep while waiting for some action to happen. After the action happens, another thread sends a signal. If there are any threads asleep, they will be awoken and given the chance to process the data of the action.

The implementation of the results management functions is illustrated as follows:

public void AddResult(IResult result) { Monitor.Enter( _results); _results.Enqueue( result); Monitor.Pulse( _results); Monitor.Exit( _results);

}

private IResult GetSingleResult() { IResult result = null;

if( _results.Count > 0) {

result = _results.Dequeue();

}

return result;

}

public IResult GetResult() { Monitor.Enter( _results);

IResult result = GetSingleResult(); Monitor.Exit( _results);

return result;

}

public IResult GetResultWait( int timeout) { IResult result = null;

Monitor.Enter( _results); result = GetSingleResult(); if( result == null) {

Monitor.Wait( _results, timeout * 1000); result = GetSingleResult();

}

Monitor.Exit( _results); return result;

}

1.For multithreaded programming for Java, I recommend reading Doug Lea’s Concurrent Programming in Java (Addison-Wesley Professional, 1999). For .NET, the book .NET Multithreading by Alan Dennis (Manning Publications, 2002) is available.

330

C H A P T E R 1 0 ■ I N F I N I T E D A T A P A T T E R N

There are three public methods: AddResult, used to add a result to the results queue (_results); GetResult, used to return a single result; and GetResultWait, used to return a single result where the method will wait for a result. In detail, GetResultWait checks whether any results are available; if not, the method puts itself to sleep and waits until there are results available. The data member _results is not illustrated, but is defined as a collection. For each of the public methods, the first action is to call the method Monitor.Enter, which acquires a lock based on the object instance _results. At the end of each of the public methods, the method Monitor.Exit is called to release the lock based on the object instance _results. The code between the Monitor.Enter and Monitor.Exit method calls is synchronized code in which only one thread may perform actions.

Synchronization and how to use it is easy to follow for all methods. What is more complicated is the signaling of the waiting thread. When the method GetResultWait executes a Monitor.Wait, the method has control of the lock. No other method may add or remove results from the collection. If the method GetResultWait realizes that there are no results in the collection, the method Monitor.Wait is called, putting the thread executing GetResultWait to sleep. In the method implementation Monitor.Wait, there is a time-out, which means that the sleep of the thread will not be infinite. A time-out causes an automatic reawakening of the thread even if no signal has been sent. When the thread reawakens, it needs to check whether the reawakening was due to a signal or time-out, and in the case of the method implementation, the collection is tested for available elements.

A signal to reawaken a sleeping thread is executed by calling the method Monitor.Pulse, but from a thread that is not sleeping. In the example, it is the method AddResult.

What is not obvious from the code is what happens to the lock when a thread is put to sleep and then awakened. If a thread goes to sleep while keeping a lock, no other threads could execute because the other threads would be waiting for the thread to awaken. The solution of a monitor is to give up the lock when a thread goes to sleep. When a pulse is sent, the reawakened thread does not execute immediately. The reawakened thread puts in a request for the lock before continuing execution. Thus, when a signal is pulsed, the reawakened thread will execute only after the lock has been acquired.

Using monitors as a synchronization mechanism is imperative because you want to implement the requirements of the Persistent Communications pattern without having to waste resources. While a monitor is waiting for a result, it is using the least amount of resources possible. You may be wondering whether monitors could also have been used to implement the background thread. And the answer is yes. Though, ideally, for the background thread, a thread pool would be a better solution. A thread pool makes it simpler to implement a producerconsumer architecture.

Implementing the Task

The last piece of code that needs to be explained is the task itself. In the example, that means explaining the class Calculator, which is illustrated as follows. Some parts of the class have been deleted for clarity:

public class Calculator : TaskManager.ITask { private long _transactionIdentifier; private long _number;

public long TransactionIdentifier {

C H A P T E R 1 0 ■ I N F I N I T E D A T A P A T T E R N

331

get {

return _transactionIdentifier;

}

set {

_transactionIdentifier = value;

}

}

public void Execute( TaskManager.ITaskManager mgr) {

mgr.AddResult( new PrimeNumberData( 1, _transactionIdentifier)); for( int c1 = 2; c1 <= _number; c1 ++) {

if( IsPrime( c1)) { mgr.AddResult(

new PrimeNumberData( c1, _transactionIdentifier));

}

}

}

public Calculator( long number, long transactionIdentifier) { if( number < 1) {

throw new IndexOutOfRangeException( "Number must be greater than 0");

}

_number = number;

_transactionIdentifier = transactionIdentifier;

}

}

The constructor of Calculator accepts two parameters: the number to be calculated and the transaction identifier. In the example of Calculator, the runtime data is copied via the constructor parameters, but it does not need to be. The runtime data could be assigned via a property or method. The task manager does not assign the runtime data, and that needs be assigned in some other fashion. The most logical is in the implementation of the ITaskData. InstantiateTask method. In the example, the constructor referenced the task data explicitly, but the constructor could have been written as follows:

public Calculator( ActionData data) { ... }

The class Calculator implements the ITask interface, which means the property

TransactionIdentifier and method Execute are implemented. The property Transaction Identifier is a simple property that assigns the data member _transactionIdentifier. In the implementation of Execute, results are added by using the mgr.AddResult method. The first thing that the Execute implementation does is add the prime number 1 to the result list. Then a loop is started, where each number up to the maximum prime number is iterated and tested to see whether it is a prime number. The method IsPrime is not illustrated; it is a simple calculation to test whether a number is a prime number. If a number is prime, it is added to the results by using the method mgr.AddResult.

The implementation of the task is the last piece of the Infinite Data pattern. At this point, it is possible to execute the application and start generating prime number sequences.

332

C H A P T E R 1 0 ■ I N F I N I T E D A T A P A T T E R N

Pattern Highlights

The implementation of the Infinite Data pattern is largely dependent on the server implementation because the server is responsible for generating the data. The client has the responsibility of creating the correct task data and associating the results with the submitted task data.

The following points are the important highlights of the Infinite Data pattern:

•The pattern is used to generate data on a piecemeal basis.

•The pattern is useful in those situations where the executing task can generate data as it does its work. For example, when using a relational database that supports piecemeal results, it is necessary to use asynchronous callbacks.

•Synchronization and background threads or processes need to be used. It is important to understand concurrency issues so that deadlocks do not occur.

•Sending and receiving XML messages, and associating data types and tasks, requires a certain amount of automation. XML schemas are very helpful.

•Even though the preferred format is XML, a format such as JSON would be useful when implementing this pattern because quite a bit of serialization and marshaling is involved.

•The basis of the Infinite Data pattern is the Persistent Communications pattern.

C H A P T E R 1 1

■ ■ ■

REST-Based Model View

Controller Pattern

Intent

The REST-Based Model View Controller pattern is used to access content that is external to the web application and used to transform the content so that it appears as if the web application generated it.

Motivation

Every application, whether it be on the Web or in a traditional form, has a purpose and solves either a single or multiple problems. Features of an application tend to be specific to that application and do not relate to other domains. A word processor is a word processor, and an e-mail program is an e-mail program. Each application is responsible for its own data and user interface. The question then arises: why can you not take the contents of a document and press a button to convert it into an e-mail, or vice versa? Why must one application be separate from another application? The typical solution for converting an e-mail into a document is to use Copy and Paste to transfer the contents from one application to another, which works quite effectively but requires an extra step.

Now imagine that an application had the capability to integrate content or functionality from another application and make it part of the original application. Such a solution would look like Figure 11-1.

The application in Figure 11-1 is called Lilina, which is a blog news aggregator. Lilina can be used to read multiple blogs and present them as web pages. What makes Lilina unique is its ability to search for blog entries by using the Google search engine and to present those results as part of a blog entry. If you think about it, Lilina is a unique next-generation application in that it has the ability to combine multiple streams of information (blogs and Google search) into a single stream. In a nutshell, Lilina is an example of the REST-Based Model View Controller pattern.

With the existence of the XMLHttpRequest object, using the REST-Based Model View Controller pattern might seem unnecessary. After all, the XMLHttpRequest object could be used to integrate content from various sources. However, the truth is that it is not possible at a technical level to easily integrate content from various sources, because of the same origin policy.

333

334

C H A P T E R 1 1 ■ R E S T - B A S E D M O D E L V I E W C O N T R O L L E R P A T T E R N

Figure 11-1. Example application that integrates external functionality

The same origin policy was described in Chapter 2. Essentially the main idea behind this policy is to not allow a JavaScript script to make a cross-domain script call (XSS).1 The same origin policy exists to enhance security and should not be thought of as a programmatic inconvenience. History has shown that hackers can and will hijack websites and cause grief to users if they are able to.

Another two reasons for using the REST-Based Model View Controller pattern are to create a consistent user experience and to not overload the web browser with unnecessary business logic. In Figure 11-1, the HTML content looks and feels like a single application, even though multiple data streams are integrated into a single HTML stream. The server does this by extracting the important pieces from an individual data stream and then using the important pieces as the basis of the new HTML content.

In theory, everything that the server does, the browser can do, and that includes the extraction and transformation of information. Although a web browser is a useful piece of software capable of running sophisticated scripts, that does not mean that a 2-megabyte JavaScript file should be downloaded and executed. A web browser should be considered an intelligent thin client. Also, as you will see in the “Architecture” section of this chapter, the purpose of the REST-Based Model View Controller pattern is to offload processing tasks from the client to the server.

1. http://en.wikipedia.org/wiki/XSS

C H A P T E R 1 1 ■ R E S T - B A S E D M O D E L V I E W C O N T R O L L E R P A T T E R N

335

Applicability

Thinking of a scenario of when to use the REST-Based Model View Controller pattern is not difficult. You need to use it when you want to access content that is not available in the currently referenced web application because of the same origin policy. Therefore, this pattern might seem like a hack used to get around something that gets in your way when developing applications. However, that is an incorrect assumption; the purpose of the REST-Based Model View Controller is to make it possible to combine single or multiple streams and expose them as a single stream that fits into the architecture of the user-defined web application.

You use the REST-Based Model View Controller pattern in the following contexts:

•To access a data stream that cannot be accessed by the client because of same origin policy restrictions.

•Defined in simple terms, as a way to convert the format of one data set into the architecture-defined data set. An example is the integration of a data stream generated by a version of the web application prior to the version being constructed. Using the REST-Based Model View Controller pattern in this fashion makes it possible to run multiple versions of the same web application concurrently without conflicts.

•As a way to integrate dissimilar technologies. For example, Google exposes its search engine by using the web service technology Simple Object Access Protocol (SOAP). SOAP can be used with HTTP, but a web browser does not understand SOAP, and hence the REST-Based Model View Controller pattern is used to convert a SOAP request into an Ajax HTTP request.

Associated Patterns

The REST-Based Model View Controller pattern is similar to an n-tier architecture and a Model View Controller (MVC) architecture. The pattern is similar to an MVC in that the model is considered other servers (for example, web sources, data sources), the controller is the controller that is managing the content from the other servers, and the view is the REST client reading the data. The REST client can be a browser, XMLHttpRequest object, or even a command-line utility. The pattern does deviate from the classical MVC with respect to being event driven. Unlike the classical MVC, this pattern does not implement an event model.

The REST-Based Model View Controller pattern can be used in two forms: synchronous and asynchronous. In synchronous form, a request is made and the client waits for the external network calls to return, aggregates the results, and presents them to the client. In asynchronous mode, a request is made and the client does not wait for the results. Instead, the results are sent to the client asynchronously.

If the REST-Based Model View Controller pattern is used in a synchronous style, the generated data will resemble the data generated by the Content Chunking pattern. If the RESTBased Model View Controller pattern is used in an asynchronous style, the generated data will resemble the data generated by the Infinite Data pattern. In addition, when using the asynchronous style, the client implements the Persistent Communications pattern.

Regardless of whether synchronous or asynchronous style is used, the Permutations pattern will need to be applied. The idea is to convert the data from one format into another format desired by the client, which is the aim of the Permutations pattern. The data that is generated

336

C H A P T E R 1 1 ■ R E S T - B A S E D M O D E L V I E W C O N T R O L L E R P A T T E R N

is not stable and will constantly change because it is based on information from the external network, and hence the Cache Controller pattern cannot be applied. One exception exists— if the external request generates information that the Cache Controller pattern can use. However, don’t count on it, and expect for the most part to not be able to use the Cache Controller pattern.

Architecture

The REST-Based Model View Controller pattern implements several patterns and the Model View Controller architecture. In its simplest form, the pattern is a wrapper to access external content. In its most complex form, it is an application in its own right.

The Big Picture

Dissect the Model View Controller aspect of the pattern and you’ll see that the model is the external content generated by the various HTTP servers. The controller performs operations on the model and generates a view, but only the view required by the client. The view is an implementation of the Permutations pattern and defines a resource and representation. Figure 11-2 illustrates an example architecture that implements the REST-Based Model View Controller pattern.

Figure 11-2. Architectural implementation of REST-based Model View Controller pattern

C H A P T E R 1 1 ■ R E S T - B A S E D M O D E L V I E W C O N T R O L L E R P A T T E R N

337

In Figure 11-2, the web browser (which will be called the view throughout this chapter) makes a request to the local server (called the controller throughout this chapter). The controller makes a request to the external servers (called the model throughout this chapter) by using a local client. The controller might make a single local client call or multiple local client calls, and it depends entirely on the application. The local client is responsible for receiving the results and converting the received results into a structure that the controller expects. The controller gathers the results, performs some business operations, converts them into a view the client expects, and then finally sends the view to the client.

The outcome of this quick overview of the architecture is that the client can call the controller and expect a specific view. The local clients adapt the remote results into local results, creating stability and robustness of the data. The controller can perform optimizations, and if necessary could integrate other sources to enhance the results. The controller could implement the Permutations pattern and the Persistent Communications pattern. The idea is that the controller can act as an aggregator that slices and dices the information retrieved. Architecturally, the various terms are assembled as in Figure 11-3.

Figure 11-3. Terms assembled into an architecture for REST-Based Model View Controller pattern

In Figure 11-3, the client calls the local server, or controller, which calls the Permutations layer, which calls the local client, which calls the remote server. The controller can embed business logic, and more importantly can act as a locally installed application. Think of it this way. A traditional client is installed on the local computer. Thus far, all Ajax applications have been thought of as executing on two separate computers. However, with the REST-Based Model View Controller, the notion of a traditional application can be implemented in that the HTTP server executing the controller is on the same computer as the web browser. The effect is a server application that communicates with other server applications, building a matrix of applications that can seamlessly interact and interchange data. Right now, to have a traditional application communicate with another traditional application is not easy and requires extra steps such as

338

C H A P T E R 1 1 ■ R E S T - B A S E D M O D E L V I E W C O N T R O L L E R P A T T E R N

Copy and Paste. However, with the REST-Based Model View Controller, a document processor could read an e-mail and directly process the data into a document, and vice versa.

The concept of location when used with the REST-Based Model View Controller becomes irrelevant because users can access their data from home, from the office, or from anywhere else. Location is irrelevant because it is replaced with a resource. Of course, you might say, “But if the resource is located at the URL http://myserver.mydomain.com/resource, the resource is locked to the server myserver.mydomain.com.” What you would be missing is that myserver. mydomain.com is a server name, and a resource that is translated by a Domain Name System (DNS)2 into an IP address. It can be pointed out that a DNS server does implement a form of the Permutations pattern. By combining a DNS server with the HTTP server-based Permutations pattern implementation, you can make a URL an abstract resource.

Defining an Appropriate Resource

Important to the implementation of the REST-Based Model View Controller pattern is the resource used to access a view of the controller. It is tempting to simplify the pattern and generate a URL that is similar to, if not identical to, the URL used to access a model. The controller would be a mirror of the remote server, thus acting as a way to get around the same origin policy restriction. Illustrating the mirror technically, to call the Alexa search engine, you can make a REST request using the URL http://awis.amazonaws.com/onca/xml. The mirrored controller URL would be http://amazon.mydomain.com/onca/xml and would be a delegation of functionality from the controller to the model. Implementing a delegation does not implement the pattern and is an implementation of the Proxy pattern.

A Proxy pattern implementation occurs when the interface exposed by the controller is identical to the interface exposed by the remote server. Note that when the word interface is mentioned, it is referenced in a code sense, and not in a user interface sense. A Proxy pattern, when implemented properly, is transparent to the client, which would suggest that the client thinks it is connected directly to the remote server.

Implementing the Proxy pattern would result in an architecture identical to Figure 11-4. Figure 11-4 shows a client making a request to perform a search on the Google and

Amazon.com search engines. The client communicates to the local server, which communicates to the remote servers Amazon.com and Google. If the local server acted like a proxy, the URL, request data, and response data would have to be unique for each search engine. The client would have to do the heavy lifting of figuring out what to send and how to process the response. This is wrong because the client should not need to do that. If a client were to do the heavy lifting, the JavaScript script would become large, complicated, and hard to maintain.

The solution is not to let the client do the heavy lifting, but to let the controller and local clients do it. Specifically, the controller has the following responsibilities:

•Defining the views available to the client

•Defining the resources used by the client

•Executing and managing the local clients used to call the remote servers

2.http://en.wikipedia.org/wiki/DNS