Return to the generated Javadoc for this class.
Return to the generated Javadoc for this package.
The class org.niggle.servlet.ServletInteraction
is the most
basic subclassing point in the Niggle framework. When you create a Niggle-based
web application, you will necessarily create your own ServletInteraction
subclass.
There are 3 basic things you will do in this subclass:
deduceLocale()
or deduceSessionInfo()
or hasValidLoginInfo()
.
The first step is trivial. It is always the same piece of boilerplate code:
public MyServletInteraction(HttpServletRequest request, HttpServletResponse response, NiggleConfig config) throws IOException { super(request, response, config); }
Of the three steps listed above, it is the last one that is optional, though if
you want to use the built-in session-tracking mechanism, you will almost
certainly have to override hasValidLoginInfo()
, since this is the
method the framework uses to determine whether it should create a session for
a user -- or politely tell him to get lost! Typically, your implementation of
this method will check if there is user/password information embedded in the
servlet request (via a parameter or maybe a cookie) and check that against an
access list in a file or database table somewhere. Aside from that, the base
ServletInteraction class has usable out-of-the-box defaults. What is likely is
that at the very end of your development process, you will want to refine these
defaults to add that extra bit of polish to your app. So, a good basic rule of
thumb is that the second step above, defining your execXXX methods, will occur
at an early stage of application development, and the last step mostly towards
the end, in a polishing-up stage.
Methods that you create with the execXXX naming pattern are considered to be the basic functional entry points into your application. Here is the basic idea:
Your app deduces the action that corresponds to this servlet request.
By default, a Niggle app will deduce the action by looking for an action=foo
parameter in the servlet request. (This scheme can be configured by overriding
the deduceAction() method, for example, but there is not any very strong reason
to do so.) In other words, a servlet request with an embedded "action=foo"
parameter is taken to be a call to the execFoo() method.
Let's consider the basic scenario of a user opening a URL like this:
http://www.myweb.com/myNiggleServlet?action=showOrder&order_id=1234
Well, the action=showOrder parameter is taken to mean that you want to invoke the method execShowOrder() on the ServletInteraction object. Your method might look something like this:
public void execShowOrder() throws IOException { if (!checkSessionInfo()) return; Integer order_id = null; try { order_id = new Integer(getParameter("order_id")); } catch (NumberFormatException nfe) { throw new MissingRecordException("no such order, expecting an integer"); } DataSource mydata = getDataSource("mydata"); Record order = mydata.get("order", order_id); if (order == null) { throw new MissingRecordException("no order with id# " + order_id); } page = getPage("showOrder.nhtml"); page.expose("order", order); }
The above code could easily be a fragment of a hypothetical web application
built on top of Niggle. The first line calls checkSessionInfo()
since we are assuming that only authorized people can view this information. Of
course, in some cases we are not concerned with this. What is also typical in a
web app is that some "actions" are restricted to authorized users,
while others are not. For instance, very typically, one can browse the messages
on a message board, but if you want to post, you must be logged in.
In any case, if the call to checkSessionInfo()
returns false, the
method can just return, since the request has already been handled at a lower
level. Assuming we go from here, we first try to deduce what order the person
wants to see. This will typically be embedded in the servlet request. We are
assuming in the above that there is an order_id=nnnn there that is an integer
lookup key. We get that from the request. In the remainder of the method, we:
As simple as that. As I never tire of saying, the Niggle framework allows you to do a lot while writing very little Java code. Note how the guts of the framework handle issues such as:
What this gives you is that, freed of most of the typical repetitive guck, your execXXX handlers end up being extremely brief and readable, allowing a lot of clarity about what the code is actually doing!
The ServletInteraction class provides a variety of hooks that you can
override to refine the behavior of your app. The ones you are most likely to
override are the ones that deal with user/session/login tracking. At the very
least, you will almost certainly want to override hasValidLoginInfo()
so that it hooks up to the relevant data. You would likely write a method
something like:
protected boolean hasValidLoginInfo() throws IOException { if (!hasParameter("user_id") || !hasParameter(password)) return false; String userID = getParameter("user_id"); String password = getParameter("password"); DataSource userData = getDataSource("users"); Record user = userData.get("user", userID); return (user != null) && password.equals(user.get("password")); }
The above realistic-looking method looks for a user_id/password combination in the servlet request. This would likely be the result of the user filling in a login form if she was not already logged in. The code in question gets a data source called "users" and then tries to fish out the record corresponding to the user_id parameter. It then checks whether there is such a user and whether the password in the record corresponds to that user.
Once you override the hasValidLoginInfo()
hook, then the other
pieces do their thing by default. It's worth eyeballing the checkSessionInfo()
method and understanding its simple-minded logic:
protected boolean checkSessionInfo() throws IOException { if (this.hasSession()) return true; else if (hasValidLoginInfo()) { return createNewSession(); } else { unauthorized(); return false; } }
The above method, checkSessionInfo() is meant to be the method you will typically call at the top of an execXXX method if you want the "action" that this method represents to be restricted to users who have been authorized. It's quite simple:
hasValidLoginInfo()
to see whether it is allowed to create a new session. The default implementation
of this method always returns true, and also sets the userID to "anon".
For login/session functionality to work in an appealing way, you will almost
certainly have to override the default implementation of hasValidLoginInfo()
.
hasValidLoginInfo
method gives a red light for
creating a session, then we call the unauthorized()
hook which, by
default, simply sends the 401 (unauthorized) code back to the client.
The important thing to realize here is that all of the methods invoked in the
above checkSessionInfo()
code are hooks that you can override to
put more polished behavior in this basic mechanism. As things stand, these
methods are basically thin wrappers around default functionality in the servlet
womb. For example, by default, the createNewSession()
method will
use the built-in session creation hook in the Servlet API. On the other hand,
you might want to override this behavior to use some other kind of customized
scheme for tracking sessions. No problem, though in that case, you would surely
have to override hasSession()
and deduceSessionInfo()
to adapt their behavior to your custom scheme.
Also, the unauthorized()
hook is something that you might well want
to override. For example, rather than sending a 401 code, it might be nicer to
present the user with a signup page or something like that.
The recover(IOException e)
method provides a hook where you can
catch general error conditions in a graceful manner. Note that the base
implementation of this method simply rethrows the exception and then this
bubbles up into the code in the servlet womb that handles it. In particular, if
the exception passed in is an instance of org.oreodata.DataException
it is very likely that you can recover fairly gracefully from the condition. For
example, if a user failed to fill in a required field, you can simply redisplay
the record. Here is an example:
protected boolean recover(IOException e) throws IOException { try { throw e; } catch (DataException e) { if (action.equals("processOrder")) { page = getPage("orderform.nhtml"); page.expose("error", e.getLocalizedMessage()); page.expose("order", e.getRecord()); } else throw e; } }
In the above example, we use the recover()
hook to allow us to
recover gracefully from a situation where a DataException was thrown as a result
of trying to process an order form. Rather than letting the exception bubble up
into the servlet engine, probably outputting a stacktrace to the client, we
simply redisplay the order form with the error variable embedded and the
associated order record. For an example of this mechanism at work, see the mini-rolodex
tutorial example.
A method that you will very often override is exposeDefaultVariables()
which gives you a convenient point at which to expose variables
regardless of which execXXX method was invoked.
The deduceLocale()
is a wrapper around the request.getLocale()
method in the Servlet API. It is very likely that you would want to override
this in a localized web app if, for example, you have stored user preference
information that says what a user's preferred locale is.
Though there is not much of a strong pragmatic reason to do it, you could
override the deduceAction()
method, which by default looks for an "action=XXX"
parameter in the servlet request. Note that if you do that, you should also
override getURL(action)
to correspond to your mapping scheme.
At this point, you will likely know that "approved" way
of sending output to the client when using Niggle is to set the
page
variable. Typically, in the course of an execXXX call,
you will have a line something like:
page = getPage("mytemplate.nhtml");
This causes the framework to fish out the appropriate page template object
and then you will set the dynamic part of the output via one or more calls
to page.expose()
.
At times, though, you may want to bypass the page template
mechanism. Often it is desirable to handle a request by redirecting to
another URL using the response.sendRedirect()
method
in the java servlet API. One thing to know is that there is a kludge
variable called hasRedirected
that, if set, indicates to the
Niggle code not to try to output a page via the page template mechanism,
that the output has been handled in another manner.
response.sendRedirect("http://somewhere.com"); hasRedirected = true;
You should also set this variable if you want to output directly to the servlet's output stream with println calls.
public void execBadPattern() throws IOException { PrintStream out = new PrintStream(response.getOutputStream()); out.println(""); out.println("I am a bad boy"); out.println(" "); out.println("I should really use a page template instead of this nonsense!!!!"); out.println(""); out.close(); hasRedirected = true; // gives the hint to Niggle not to use Page variable. }
I point out the above for the sake of completeness and in case you want to do that. On the other hand, it escapes me just why you would really want to write a method like the one above.
Document last updated 20 June 2001, by Jonathan Revusky. If you have any feedback regarding this document, feel free to write the author.
Return to the generated Javadoc for this
class.
Return to the generated Javadoc for this
package.