This document describes some of the
miscellaneous features within Barracuda, as well as how to access them.
- Detecting Client
Side Scripting

- Disabling the Back Button
- URL Rewriting
- DOM Pretty Printing
Detecting Client
Side Scripting - As you are probably aware, Barracuda can
tell you whether or not a client supports scripting by creating a
ViewCapabilities object. This information is particularly useful
when rendering components into the DOM--if a client is capable of
handling scripting we can generate a much more effective user
interface by including Javascript in the output. The problem with
this approach is that this ViewCapabilities setting only
considered whether or not the client supported scripting;
it didn't tell you whether they actually had it enabled.
This was really less than optimal.
As of Barracuda 1.0.2, we can now tell whether or not scripting
is actually enabled in the client browser! We accomplish this
without storing any information in cookies or user session. We can
even determine when the client setting changes. Here's how it
works.
First of all, whenever an HTTP request comes in to the
Barracuda system (either through the ApplicationGateway or the
ComponentGateway), we inspect the request to see if there is a
special marker parameter ($csjs) which indicates definitively
whether or not the client supports scripting. If this value
(either true or false) is present, we assume we know for sure, and
we process accordingly. If this value is not present, we still
determine the result as we used to (by looking at browser type).
You can look at ViewUtil.getScriptingType() for details...
Now, the question arises: How does this marker flag get set?
Well, when ever we render a page back to the client, any classes
that use the component framework (either ComponentGateway or
DefaultViewHandler) will allow user code to modify the DOM as
needed. Then, right before the page is written out to the client,
we add a simple piece of logic which looks at all the forms and
links in the page. For each form, we add a hidden field called $csjs
whose value is set to false; for links, we append on a ?$csjs=false.
These markers basically say "Hey, the client doesn't support
scripting!". For more details, look at ScriptDetector.prepareClientResp()...
In addition to setting these hidden values to false, we also
embed a client script (ScriptingCheck.js) in the page. When
the client page is loaded, this script will run through all the
links and forms on the client, setting $csjs values to true. This
effectively modifies the objects capable of generating HTTP
requests so that when they are invoked they will tell the server
"Hey, the client supports scripting!". Of course, if the
client doesn't support scripting, the script won't get run and the
values will never change.
With us so far? Ok, great! Now for the last key piece: What
about a user manually types in a URL, like GetLogin.event? This
link will not have the $csjs marker field, so how can the server
tell whether scripting is enabled? Well, in this case (a GET
request), the server sees that there is not $csjs marker field,
and so it immediately sends back a response that looks something
like this:
<html>
<head>
<script>
location.replace('someurl?$csjs=true');
</script>
<noscript>
<meta HTTP-EQUIV="REFRESH" CONTENT="0; URL=someurl?$csjs=false">
</noscript>
</head>
<body>
</body>
</html>
What happens in this case as that the client will immediately
send back the exact same request, but this time it will include
the $csjs marker value which will again tell us whether or not the
client supports scripting. It should be noted that we only do this
in the case of GET requests; if we are dealing with a POST, we
assume that the page probably came from the server and thus it
should really have the marker values already. You can look at ScriptDetector.checkClientReq()
for more details.
As a final point, we should talk about what happen when a
client actually changes his scripting settings mid stream.
Basically, when this occurs, the client must refresh the page.
From that point on, all submits and posts will have the new
scripting settings. While it would be nice if a refresh wasn't
necessary, there doesn't seem to be anyway to be notified
when the client scripting settings change (and at the same time,
its probably not that big of a deal since this particular use
case--user swapping scripting settings midstream--is a relatively
rare occurence).
The net effect of all this is that the ScriptingType setting in
ViewCapabilities will now be much more accurate than it used to
be, accurately notifying server side code whether or not the
client actually has scripting enabled. And if for some reason
you'd really rather just stick with the old behavior, you can
always turn off the new approach simply by setting ScriptDetector.DETECT_CLIENT_SCRIPTING_ENABLED
= false;
Cool.
Disabling the Back Button -
Barracuda also makes it easy to disable the back button within client pages. What we mean
by this is not that the button itself is physically disabled within the client browser,
but rather that a URL gesture (ie. clicking a link, submitting a form, etc) causes the
resulting page that is returned from the server to replace the current page in the browser
history. This means the user can still go backwards in the browser history, its just that
the page which generated the URL gesture is not there to go back to--its been replaced!
To get an idea of what we mean by this, you might want to look at the URL Rewriting / Back Button Example.
Now that you understand what we mean by 'Disabling the Back Button', here's how you
would utilize this as a developer. Barracuda's BAction components provide
a simple setDisableBackButton() method...if this value is set to true,
the component will be rendered so as to prevent the user from going back. What this means
is that any markup element which can be controlled using BAction components -- buttons,
links, forms, select, and input elements -- can be modified so that when the user invokes
them, the resulting page is loaded into the current page of the browser's history, thereby
preventing the user from resubmitting, etc.
At this point you may be wondering how it all works. Well, let's just say its magic.
Ok, so maybe its not THAT amazing, but the underlying logic is pretty complex. Basically,
when we wish to reload a link or submit a page and have the results loaded into the
current history location within the browser, we have to invoke some Javascript to do this.
In the case of a link (<a> element), it's pretty straightforward: we can just do a
location.replace(). In the case of a form, however, it's much more complicated since we
need to submit the form (which effectively causes a new page to be loaded). The solution
we came up with is pretty nifty.
First, we use dynamic HTML in conjunction with Javascript to create an iframe (IE,
Mozilla) or layer (NN). From there, we use Javascript to dynamically create/submit a form
to a special Barracuda ParamGateway servlet, which unloads all the form
values and caches then in the client's session. Then the ParamGateway redirects the
browser to the proper URL (which can be loaded using the location.replace approach
described earlier). When the client issues the GET request, the param values are restored
from the session cache and processing completes with no one--neither client or server--the
wiser.
The bottom line is that this approach solves the back button issues in a manner which
is easy to use and will work for a majority of developer needs.
The inspiration for all this came from Brent Ashley [brent@ashleyit.com], who has created the JavaScript Remote Scripting (JSRS) Library. We
had to modify Brent's code quite a bit, but are nonetheless deeply indebted to him for
this piece of functionality. If you think it's cool (like we do), you might want to drop
him a line and say thanks.
Obviously, a Javascript solution will have certain limitations. First and foremost, its
only going to work in browsers that support Javascript. We've tested and verified it in IE
5, NN 4, and MZ (NN 6). If you need to use it other places you may have to come up with
your own solution. It should also be noted that within NN 4, forms are actually submitted
using GET (regardless of whether the form is actually specified to use GET or POST). This
is because NN uses layers and they don't support a target of POST. This means if you have
really big forms you may run into some size problems. If you do, well, don't use the Back
Button Features.
That's about it. While its not a perfect solution, its still pretty dang useful. Enjoy!
URL Rewriting - Barracuda supports the
notion of URL Rewriting through a helper class called org.barracudamvc.core.util.http.URLRewriter.
This class provides methods used by the rest of Barracuda whenever it needs to generate a
URL. URL Rewriting ensures that the servlet can access Http Session information for
individual clients, even if the client has cookies turned off.
By default, URL Rewriting is turned off, due to a bug in Enhydra 3.x which we're
running against on the main Enhydra site. To turn on URL Rewriting, simply set URLRewriter.REWRITE_URLS = true. You can do this programatically
by modifying the actual code, or (the better way) you can set this value dynamically when
your system starts up using the Barracuda assembler file (ie. ex4.xml). To use this
latter approach, simply add a line like this to the assembler file:
<constant class="org.barracudamvc.core.util.http.URLRewriter"
name="REWRITE_URLS">true</constant>
It's also important to spell out what this approach doesn't handle. Right
now, we are only doing URL rewriting on links manipulated by Barracuda. This means that if
you have a link embedded in a template that is NOT processed by Barracuda, that link will
not get modified. I believe the XMLC DOM renderer provides some default processing to
support URL Rewriting, but we haven't researched that fully (so that may be an option for
further exploration).
DOM Pretty Printing - Barracuda's DefaultDOMWriter
supports the notion of pretty printing. To turn it on, all you have to do is instantiate
your DefaultDOMWriter with a boolean value. Alternatively, you can set the DefaultDOMWriter.defaultPrintPretty
= true to make all DOMs use pretty printing by default.
The way we handle pretty printing is by using the Tidy formatter (the version that
ships with XMLC) to reformat the resulting markup that comes back after the DOM has been
initially rendered. This means that pretty printing is not particularly efficient, and as
such should probably NOT be used except for debugging purposes.
Also note that there currently seems to be several bugs in Tidy, whereby it incorrectly
inserts spaces into <td> cells that contain only images and no text. In addition, it
seems to split <textarea> cells incorrectly (thereby inserting spaces in the control
when rendered in the browser). Both of these may have adverse affects on your display, so
you probably don't want to use pretty printing for anything other than debugging at this
point. If anyone knows how to fix these issues, please let us know.
|