WHAT’S THIS JSON YOU SPEAK OF?

Một phần của tài liệu Tài liệu Practical DWR 2 Projects pptx (Trang 133 - 150)

JSON, which stands for JavaScript Object Notation, is a simple data interchange format that is roughly similar to array notation in most C-like languages. JSON is technically a subset of the object literal notation used in JavaScript to define objects.

A quick and simple example of JSON is this:{ firstName:"frank", lastName:"Zammetti" }.

If you were to execute var p = eval(json);where jsonis the example JSON shown here, what you would have is an object with the fields firstNameand lastNamepresent and pointed to by the variable p such that doing alert(p.firstName);would result in a pop-up with the text “frank” in it.

JSON has quickly become very popular in Ajax development and is at this point perhaps the de facto standard for client-to-server communication in webapps today. Its primary benefits are simplicity in terms of creation and consuming, lightweight (as compared to the typical alternatives such as XML), and the fact that it’s still relatively human-readable. It, like XML, is essentially self-describing, assuming the person or system generating it isn’t brain-dead.

It allows for common structures needed such as arrays (lists) and maps, as well as arbitrarily complex nesting depths to allow for object hierarchies.

While it is popular in Ajax development, it is by no means limited to that domain. In fact, JSON, since it’s just a plain text format, can readily be used in many circumstances where you have data interchange needs.

Now, let’s get back to those JavaScript comments in the response. These are the key to how DWR handles this final “fringe” category of failures. Imagine if you will what would hap- pen if a user is on a web site that uses DWR and he or she is sitting idle for a few hours (that user shouldn’t have eaten that gas station tuna fish sandwich for lunch!), and now he or she hits a button that fires off an Ajax request. Assuming the application was written with any semblance of normalcy, the session will time out. As I’m sure you know, that timeout will be handled by the container (or web server if there’s one in front of it) long before DWR or the remote object that’s being called is hit. So, what’s returned to the client-side DWR code mak- ing the call? Well, it will depend on the server configuration, but clearly it won’t be a proper DWR request.

DWR will determine this, primarily, by looking for those comment lines in the response. If they aren’t present, this is considered an “improper” response from DWR’s point of view. Like all other conditions so far discussed, you have the ability to handle this situation if you wish.

The alternative is to let DWR silently eat them, which probably isn’t what you want for a robust application.

The Mechanics of Handling Exceptional Situations

So, now that you understand the various scenarios that can lead to warnings, errors, excep- tions, and improper responses, the next logical question to answer is how do you actually code for them? As with most things in DWR land, it’s very simple. In fact, the very simplest form (aside from doing nothing and letting the DWR defaults be in effect) is this:

dwr.engine.setErrorHandler(someFunction);

For all errors that occur, someFunction()will be called, and you can take whatever action you deem necessary at that time.

Similarly, if you wish to handle warnings, you can do the following:

dwr.engine.setWarningHandler(someFunction);

Finally, to handle those improper responses that might possibly come back from the server, you need to set up what’s called a textHtmlhandler, like so:

dwr.engine.setTextHtmlHandler(someFunction);

Now, you’re probably wondering about handling exceptions, and of course you can do that too, but there is a difference. While you can handle errors, warnings, and improper responses at a global level, you cannot do so for exceptions. You must handle them on a per- call basis, which leads to the fact that you actually can handle any of these types on a per-call basis (or on a per-batch basis, except for exceptions, which are always on a per-call basis, even when a call is made as part of a batch). All you need to do is make sure to use the metadata object call mechanism and specify the error/exception/warning/improper response callback.

Here’s an example:

MyRemoteClass.someMethod(param1, param2, { callback : function(response) { /*Do work*/ },

errorHandler : function(errMessage, exception) { /*Do work*/ } });

Again, while exceptions cannot be handled at the batch level, all other types can be, and the syntax for that is very similar to the per-call basis:

dwr.engine.beginBatch();

MyRemoteClass.someMethod(param1, param2, function(response) { /*Do work*/ });

// More calls

dwr.engine.endBatch({

errorHandler : function(errMessage, exception) { /*Do work*/ } });

Another Word on Exceptions

One final thing to discuss about exceptions is how they are marshaled from the server. In short, DWR takes the same “deny by default” tact as it does with remoting of classes. However, this would immediately lead to the conclusion that when an exception occurs, you’d in fact get no response back, not a meaningful one anyway, and that’s clearly not the right answer.

So, by default, DWR will use what it calls the MinimalistExceptionConverter. This will result in a response that DWR recognizes as an error, but it will contain no information about the nature of the error. This is done for security reasons (imagine what a decent hacker could learn about your application just by the information contained in the error messages if he or she continually caused errors on purpose!).

However, you may well want to have more information than this, especially during devel- opment. So, you can configure a converter in dwr.xmlsuch as

<convert match="java.lang.Exception" converter="exception" />

Now, any exception matching this (any descendant of java.lang.exceptionin this case) will result in a string of JSON being returned such as this:

{ javaClassName : 'java.io.IOException', lineNumber : 109,

publicId : 'pid',

message : 'Couldn't read file' }

That’s clearly a lot more useful to you as a developer! However, you may want something in between because in a production environment you may well want more information than the default, but not quite as much as in development. Fortunately, the converter allows you to have more fine-grained control over what information is presented back to the caller, and again, it’s nothing more than some added configuration:

<convert match="java.lang.Exception" converter="exception">

<param name="include" value="message,lineNumber" />

</convert>

One other thought factors into security here, and that’s stack trace elements. Returning stack traces to users, aside from being bad in terms of usability, is a potential security risk. So, by default, DWR won’t include stack trace elements. If you’d like to see that, you need to allow DWR to marshal them as well by adding this configuration element:

<convert match="java.lang.StackTraceElement" converter="bean" />

As with any time you decide to dump a stack trace, realize that it can be rather long and verbose, so it’s definitely something you want to use with caution.

Help from Elsewhere: Accessing Other URLs

Let’s say you have a class that you are calling via DWR, and in this class you need the services of another part of the application. However, this part of the application isn’t exposed via DWR.

For instance, you need to show a particular entry form to the user, so all you really want to do is insert some markup into a <div>on the page. However, the form is created dynamically using Velocity templates. You may think DWR can’t really help you here, but in fact it can.

DWR gives you the ability to fetch content from a URL and return it as the result of a method execution. For instance, examine the class in Listing 3-3.

Listing 3-3.Reading from Another URL and Returning the Result package app;

import java.io.IOException;

import javax.servlet.ServletException;

import org.directwebremoting.WebContextFactory;

/**

* A class to read from a URL and return the contents.

*/

public class URLReader {

/**

* Read a URL and return its contents.

*

* @return The contents of the URL.

* @throws ServletException If anything goes wrong.

* @throws IOException If anything goes wrong.

*/

public String read() throws ServletException, IOException { return WebContextFactory.get().forwardToString("/another.jsp");

} // End read().

} // End class.

Now, examine the call to this class:

function readURL() { URLReader.read(

{

callback : function(inResp) {

document.getElementById("divURL").innerHTML = inResp;

} } );

}

So, what happens is this: the read()method of the URLReaderclass is called. It uses the standard servlet API forward mechanism under the covers to get the contents of the specified URL, which in this case is another JSP within the same webapp. That content is then returned as a string as the result of the read()method’s execution. This content is returned to the client, which then inserts it into the divURL <div>.

You don’t need to type this in yourself: included in the download source code bundle is an application named anotherurlwhere this code is taken from. Again, laziness has its virtues!

This is a very handy function when you need to return markup. There’s no need to write code in classes to generate it, unless you’re into that sort of thing, but it pretty quickly leads to unmaintainable, ugly code, so it’s better to avoid it.

The bad news is that this is subject to the same rules and limitations as the standard servlet API forwarding mechanism, so you can’t use this to fetch remote content, unfortu- nately. But, it solves the problem of how to generate markup very well, so you should keep it in mind for sure.

Turning the Tables: Reverse Ajax

Just when you start getting your brain wrapped around this whole Ajax thing, they go and change the rules! That’s exactly what reverse Ajax is. Figure 3-6 shows reverse Ajax graphically, a nice summation of how it works (it in fact kind of jumps the gun a bit by giving you an idea of what the code looks like that makes it work . . . we’ll be getting to that after some discussion of reverse Ajax).

Figure 3-6.Graphical overview of reverse Ajax in DWR (used with permission of Joe Walker, DWR creator)

Reverse Ajax is something fairly new, coming to prominence only really in the past few months. The basic concept is that instead of the client having to pull information from the server, the server can push information directly to the client. This is all in an effort to over- come the one limitation of Ajax that persists from the “classic” web model, namely that real-time information is still not technically possible because there is still a requirement that the client has to contact the server and ask “Hey, dummy, has anything changed?” and then update the page (or some section thereof ) if anything has. While you can do this quickly enough to make it seem it’s happening in real time, it really isn’t. What’s needed is a way for the server to contact all the browsers viewing its contents to announce when changes have occurred.

Reverse Ajax is a way to make that dream a reality. Like Ajax itself, it’s not a specific tech- nology but a technique to use existing technologies in some unusual ways.

As you will no doubt recall, the classic Web, that is, non-Ajax applications, has a specific flow of events that characterizes it. Simply stated, a user action on the client results in a request to the server, then some processing occurs on the server, and a response is returned to the client, the response being an entirely new UI view. This continues ad infinitum, until the user decides to leave the web site. In Figure 3-7 you can see a graphical representation of the sequence of events.

Figure 3-7.Basic sequence of events for the classic Web

An Ajax-based application has a slightly different sequence, and Figure 3-8 shows that.

There, we can see that some user activity results in a call to some client-side Ajax engine, whether that’s our own JavaScript code or some library. This engine makes a request to the server, which does its processing as in the non-Ajax model, then returns the response. The response is handled initially by the Ajax engine, which then typically calls on some client code to update the page. Once again, this sequence continues indefinitely until the user navigates away from the page.

Reverse Ajax presents yet another sequence of events, but it’s one based on the Ajax dia- gram in Figure 3-8, in which there is a two-way flow of information, the usual client-initiated flow and the new server-initiated flow (we’ll see some diagrams in a moment, I know you’re just awaiting the pretty pictures!).

Now, here’s the thing: reverse Ajax is essentially an illusion! To be sure, it’s a well-done illu- sion, but an illusion nonetheless. Within the confines of current HTTP technology, there is no way to truly push information from a server to a client because the protocol is fundamentally stateless, and once the client-initiated connection is broken, the server no longer knows about its clients. There is no notion of an “always-on” connection from client to server, regardless of who may have initiated it.

So, you can’t have true push technology, but you can actually emulate it surprisingly well!

There are three techniques at present for performing this magic trick, and DWR supports all three. In addition, two of them fall in the category of “active” reverse Ajax, while the third is considered “inactive” reverse Ajax. Let’s take a look at each and what DWR has to offer.

Figure 3-8.The basic sequence of events in the Ajax world

Polling

The polling technique is, strictly speaking, not specific to Ajax and has been around for quite some time longer. This is the simplest form of “server push,” and you are no doubt already aware of it, even if the term is unfamiliar.

Image a plain old web page, no Ajax or anything special. Imagine you use a <meta>refresh tag to continually update the page every few seconds. This is polling in action! The client is continually polling the server for changes and displaying the new information (technically it displays whatever the server reports as the current information, which may or may not be new as compared to what the user was seeing before the last poll event, but the appearance is that new information is displayed while unchanged information is still displayed unchanged).

You can do the same thing with some simple JavaScript on a page that continually refreshes the page. It’s all the same thing, and it’s all polling.

In the Ajax world, the flow of events is slightly more complex, but still essentially the same, as you can see in Figure 3-9.

In this diagram, you can see the first three poll events occur without any user interven- tion; the polling client, or Ajax engine, is initiating the request independent of anything the user may be doing. Also note that those first three poll events do not make calls to the browser UI when the response is received. This indicates cases where no update was required because no new data was present.

This is one of the two active reverse Ajax methods because there is an automatic action of some sort involved in getting updates from the server to the client. The other active method is called Comet.

Figure 3-9.The sequence of events in the polling technique

Comet

Comet, the second active reverse Ajax method, is of course so named so as to be a counterpart cleaning agent to Ajax (give that a second to process if it didn’t make sense, and if you still don’t get it, take a break, head on down to your local supermarket, and look through the products in the cleaning supplies aisle . . . although it may not be something found in every country, as my technical reviewer pointed out, so I’ll save you the time and tell you that Comet is a cleaning product sold in the United States that comes in a distinctive polished green can), and it is adequately, if a little confusingly, described in Figure 3-10.

The basic concept is very simple: a client makes a request to the server, and the server starts to respond. It then continues to respond at an exceedingly slow pace. So slow in fact that the connection is held open indefinitely because, while it’s moving very slowly, it’s still active.

Think about that: we’ve essentially gotten around the HTTP limitation of not having a static connection from client to server! Data can then flow across this connection in both directions, giving you the potential for truly real-time updates initiated by the server.

Many people don’t consider the polling technique to be reverse Ajax at all, and that’s not entirely unfair, but Comet most definitely is, and in fact was the catalyst for the term reverse Ajax becoming well known in the first place.

The term Comet appears to have first been coined by Alex Russell, the original brains behind the very popu- lar Dojo toolkit (http://dojotoolkit.org). I just wanted to give credit where credit was due . . . for all DWR’s cool reverse Ajax support, including Comet, it wasn’t an invention of Joe or the rest of the DWR folks (although there is an argument to be made that they’ve done it best to date).

Figure 3-10.Comet sequence of events

If you’ve done any amount of enterprise development, you may well be asking a question or two right now: Does reverse Ajax, Comet specifically, scale? Will it kill my server? The answer is a resounding maybe on both counts.

It should be clear that holding connections open for any length of time is potentially a recipe for overloading a server and running out of request processing threads. This is referred to as thread starving the application server, and it is most definitely a Bad Thing™. Fortunately for us, DWR has mechanisms to deal with this.

DWR offers three different modes of reverse Ajax (aside from the polling/Comet/piggy- backing modes, two of which we’ve seen and one—piggybacking—that we’ll see next . . . call these three new modes “submodes” if you wish) when you’re dealing with the active schemes.

The first mode is full-streaming mode. This is the default mode when reverse Ajax is turned on, and it works by closing the connections every 60 seconds or so to ensure the browser is still active (at which point the connection would be automatically re-created to give the appearance of an always-active connection). DWR also implements a rudimentary form of throttling on the server where it will dynamically adjust the connection time and timeout based on the server load.

The second mode is early closing mode. In this mode, things work similarly to full-stream- ing mode except that the connection is held open only for 60 seconds if there is no output to the browser. Once output occurs, DWR pauses for some configurable time before closing the connection, forcing proxies to pass any messages on. You can configure the time delay between the first output from the server and the close of the connection, which by default is 60 seconds as in full-streaming mode.

The downside of early closing mode is that for applications with a high rate of output, it can cause a huge hit count, and even in applications with a low output rate, an event could cause all connected browsers to simultaneously reconnect. In situations with a low output

Một phần của tài liệu Tài liệu Practical DWR 2 Projects pptx (Trang 133 - 150)

Tải bản đầy đủ (PDF)

(570 trang)