Monday 29 December 2008

Seaside 2.9: Exception Handling

Ok, I promised well over a month ago to start documenting some of the new bells and whistles in the Seaside 2.9 Alpha series. I thought I'd start things off with a discussion of the new exception handling mechanism.

Have you ever wanted to customize the look of Seaside's error pages? Want to send yourself an email whenever an exception is raised? Maybe save a copy of the image on errors so you can look at the stack later? It has been possible for ages to implement a custom error handling class for your Seaside application but the process was not necessarily obvious and you were limited to catching subclasses of Warning and Error. In the upcoming Seaside release, we have cleaned up the exception handling code to really simplify the error handlers that come with Seaside and to make your custom handlers simpler and more flexible.

Basic Usage

To create your own exception handler, the first question is what class to use as your superclass. If you are just interested in customizing the appearance of error pages or performing some additional action when an error occurs, you probably want to subclass WAErrorHandler. If your requirements are more complex, you may want to subclass WAExceptionHandler directly (see Advanced Usage below).

Subclassing WAErrorHandler

WAErrorHandler is already configured to handle Error and Warning and is a good starting point if you don't need to drastically change the error handling behaviour. In your custom subclass, you can implement #handleError: and #handleWarning: to define the behaviour you want in each case. The methods take the exception (either an Error or Warning respectively) as a parameter and should ultimately either resume the exception or return an instance of WAResponse. By default, both methods call #handleDefault: so you can provide common implementation there.

For example, assuming you had defined a routine to notify yourself of errors by email, you could provide an implementation like this:

handleError: anError
self notifyMeOfError: anError.
^ WAResponse new
internalError;
contentType: WAMimeType textPlain;
nextPutAll: 'Eek! An error occurred but I have been notified.';
yourself

Returning an HTML Response

You could of course provide an HTML response but you would want to make sure your HTML is valid (and that means a head, a title, a body, etc.):


handleError: anError
^ WAResponse new
internalError;
nextPutAll: '<html>
<head><title>Error!</title></head>
<body><p>An error occurred</p></body>
</html>';
yourself

If you want to use the Canvas API, you can subclass WAHtmlErrorHandler instead. Just implement #titleForException: and #contentForException:on: (it gets passed the exception and a canvas) and you're done.

Using Your Exception Handler

Once you have written your exception handler, you need to configure your application to use it. Open your web browser and navigate to the Seaside configuration page for your application. There you will find a preference called 'Exception Handler' and you should be able to choose your new class from the list.

Alternatively, from a workspace, execute something like:

(WAAdmin defaultDispatcher entryPointAt: 'myapp')
preferenceAt: #exceptionHandler put: MyHandlerClass

Advanced Usage

If your needs are more complex than the above, you may want to subclass WAExceptionHandler directly. An exception handler needs to do two things:

  1. choose which exceptions to handle
  2. define how to handle them

Selecting Exceptions

The easiest way to select which exceptions to handle is to implement #exceptionSelector on the class-side of your new handler (note: in Seaside 2.9a1 this method is named #exceptionsToCatch). Your #exceptionSelector method should return either an ExceptionSet or a subclass of Exception. You probably want to continue handling the exceptions handled by your superclass so your implementation might look like:

exceptionSelector
^ super exceptionSelector, MyCustomError

If your needs are somehow more complex, look at implementing #handles: and #, yourself on either the class- or instance-side, as appropriate (note: these two methods do not exist in Seaside 2.9a1).

Handling Exceptions

When an exception is signaled and your handler indicates (via #handles:) that it wishes to handle the exception, #handleException: is called and passed the signaled exception. To define your exception handling behaviour, simply implement #handleException: on the instance-side of your handler. Note that the same handler instance is used throughout a single HTTP request even if multiple exceptions are signaled.

The #handleException: method is expected to return a WAResponse object. It may also choose to resume the exception, cause the Request Context stored in its requestContext instance variable to return a response (by using #redirectTo: for example), or otherwise avoid returning at all; but if it returns, it should return a response.

Internal Errors

WAExceptionHandler also provides a class-side method called #internalError:context:. This method creates a new instance of the handler and calls #internalError:, which should generate a very simple error message. This method is used whenever the exception handling mechanism itself signals an error as well as any other place in the system where we cannot be certain that a more complex error handler will succeed. This method should not do anything that has potential to raise further errors.

Setting Up an Exception Handler

Exception handling for Seaside applications is set up by WAExceptionFilter (more information on filters in a later post) and you do not need to do anything else if you are using WAApplication and WASession. If you are implementing your own request handler, however, and want to use the exception handling mechanism, you will need to set up an exception handler yourself.

Although you could do everything yourself, the easiest thing is to call #handleExceptionsDuring:context: on your handler class, passing it the block you want wrapped in the exception handler and the current WARequestContext object. You can look at RRRssHandler for an example.

Examples

The Seaside distribution includes a few exception handlers that you can look at for examples of usage. Beware of WADebugErrorHandler and WAWalkbackErrorHandler, though, as they do crazy things to get debugging to work the way we want in Squeak; although they are well commented and may be interesting to look at, they are probably not good examples of general usage. The Seaside-Squeak-Email package includes WAEmailErrorHandler, an abstract class that can be subclassed and used to send an email whenever an error occurs.

Monday 15 December 2008

Christmas giving

I just stumbled across this site: http://www.nochristmasgiftsthisyear.com/

As it happens, I've been reducing my Christmas gift giving over the past few years anyway, agreeing with more and more people just to forgo the exercise. I love giving gifts (really) but have been finding searching them out, selecting them, and buying them increasingly stressful. At the same time, when people ask me what I want or need, I have a hard time coming up with anything much and the same seems to hold true for everyone else. In order to perpetuate mass consumerism, this situation seems to have brought about the proliferation of gift cards. I don't have a problem with gift cards per se (they've been around in the form of gift certificates for ever), but the fact that I can now buy gift cards for a hundred different stores at the Safeway checkout counter makes me feel like things have gone too far.

Sure, I remember many childhood years, waking up with eager anticipation of gifts to come–at least one of them usually big and/or expensive. There were even a couple of years there, not too long ago, when Christmas seemed to have lost its luster and I just couldn't eke much enjoyment out of it.

But things have mellowed again and Christmas has been reborn. Now it's about cooking "Christmas" dinner (weeks in advance to fit everyone's schedule) with friends. It's about brisk walks down brightly-decorated streets and the smell of a wood fire (where still permitted by law). It's about spending time with family. And sure, sometimes it's still about giving gifts but the joy is back (on both sides) because it's now about an occasional gift that you didn't even expect. One of the most meaningful gifts I received was an album of family recipes and baby photos. In fact, I think that's when I realized that my perception of Christmas had changed.

I don't know if it's just the result of aging or if society is actually start to push back against consumerism-for-its-own-sake but it feels good. And if you still feel you need to get me a gift, try something consumable: wine, herbs, spices, oils. It's so nice to get a bottle of wine I've never tried and would never have bought. And if I don't like it, at least it's gone instead of staring at me for the rest of time from a dusty shelf or the back of a closet.

Thursday 11 December 2008

The will of the people

Steven had a post about the recent Canadian political events wherein he concludes:
So best I can tell, when the Governor General does allow the opposition to take power without an election there is noise, controversy and eventually change so it cannot happen again. And when the Governor General does not give power to the opposition then history seems to just continue along. I guess I must have missed McArthur's convention.
Perhaps this is nitpicking, but we're not talking about the Governor General giving power to the opposition; we're talking about the Governor General giving power to a new coalition, one with the support of the majority of parliament.

Now I agree that the Governor General has this option but certainly no obligation to choose it. On the other hand, I still can't fathom why we would want another election right now: Canadians elected 308 MPs to represent them and if the majority of them want a change in parliament then that is the will of the people, particularly this soon after an election. The incessant cries of Canadians who seem to think they live in an the US and therefore elected the Prime Minister are driving me nuts.

I also disagree that the Governor General has lost her authority to act against the will of the Prime Minister. By convention, she normally follows the advice of the Prime Minister, but, at least as far as I'm concerned, this is only in his role as the head of parliament. The Prime Minister in Canada is not a political office; he does not have any special privileges and should not; the Governor General is bowing to the will of the people (as represented by their elected MPs in parliament), not to the Prime Minister.

The Governor General has a tenuous position in that being seen to oppose the will of the people would likely result in a swift call to remove what has become a largely ceremonial position. However, I also think we have a history of intelligent, reasonable, and competent Governor Generals and I'm glad the position is still not technically powerless. In this case, I think calling a timeout is a reasonable decision, allowing both sides a chance to back down. If the non-confidence vote goes ahead in the new year, though, I'd hope she won't waste our time and money with another election.