$URL: svn+ssh://christianc@svn.forge.objectweb.org/svnroot/barracudamvc/Barracuda2/trunk/WEB-INF/src_docs/architecture/event/overview.html $ - $Revision: 125 $
Put all your content below this line

Long Running Events

The purpose of this document is to describe how Barracuda makes it very easy to handle long running tasks.

Defining the Problem

In many cases, the pages you generate will take only a few seconds to create and return to the user - the user submits a request... Bang! They get the response and life is good. 

Unfortunately, however, there are often situations (like generating large reports) which not only take a long time to complete, but also place a heavy load on the server. In cases like this, its quite common for the user to submit a request and wait a few seconds/minutes. If the request seems to be taking longer than it should... (yes that progress icon up in the right hand corner says the page is loading, but its not here yet!!!!)... they re-submit the request again. And again, and again, and again. And your server which was already churning to handle this one really big task now has thirty more requests for the exact same huge report. Ugh!

What is really needed is a way to easily give the user some kind of progress indicator, to show them "hey, the server is doing something...here's how long its probably going to take, and here's where we are in the process. Not only that, but it should also make it really easy for the user to Cancel that request (and actually stop the running process on the server, so it doesn't consume valuable resources). Finally, a feature like this should be very easy for developers to implement - as an afterthought, almost; it shouldn't require any sophisticated pre-planning on the part of the developer, because often you won't know how long tasks are going to take until you roll them out to testing. So we need an easy way to say..."hmm, that report is taking a while to run...let's configure it to show the user a progress dialog."

Enter the new Barracuda Long Running task support.

Seeing it in Action

Ok, want to see what it looks like? 

What you should see here is a temporary page that looks something like this:

The screen will refresh automatically every 5 seconds, but the user can refresh anytime simply by clicking the Refresh link. The user can cancel the process by clicking Cancel. When the process completes, the screen will reload with the generated result (in this case the same page we started from). 

Wow. Pretty slick. So what's it take to make it happen?

Making it Happen

Making it happen is a piece of cake. All we have to do flag our event as implementing a new Barracuda interface called LongRunning, and the way we do this is through the events.xml definition. Here's what the event declaration file looks like for our sample app:

<build-events pkg="org.barracudamvc.examples.ex1.events">
  <control-events>
    <event name="SomeSpecialEvent"/>
  </control-events>
  <req-events>
    <event name="LocalRequestEvent">
      <event name="LoginScreenEvent">
        <event name="GetLoginScreen"/>
        <event name="AttemptLogin"/>
      </event>
      <event name="MainScreenEvent">
        <event name="GetMainScreen"/>
        <event name="GetMainScreenSlowly"
               template="LongRunning.template"
               on_cancel="new GetMainScreen()"
               eta="30" />
        <event name="ExpireSession"/>
      </event>
    </event>
  </req-events>
  <resp-events>
    <event name="LocalResponseEvent">
      <event name="RenderEvent">
        <event name="RenderLoginScreen"/>
        <event name="RenderMainScreen"/>
      </event>
      <event name="ErrorEvent">
        <event name="BadUsername"/>
        <event name="BadPassword"/>
        <event name="UnknownLoginError"/>
      </event>
    </event>
  </resp-events>
</build-events>

Basically, all we have to do is add define a couple of additional parameters and recompile:

At a minimum, you need these 3 attributes to mark an event as LongRunning and provide the necessary information for it to be dispatched in the background.

More Advanced Configuration

Ok, so we can see that basic configuration is really easy; what if we want to do more advanced configuration? That's actually quite straightforward as well...

System-wide Configuration

There are several settings which you can configure on a system wide basis. LongRunningEventGateway has several static constants which can be configured through the ObjectRepositoryAssembler:

The most important of these is the DEFAULT_TEMPLATE setting. Since your app may have a distinct "Look and Feel" (L&F), you may want the progress dialog to look like it "fits" with your app. For instance, in our applications, our progress dialog looks like this:

The way we do this is by overriding the DEFAULT_TEMPLATE setting to point our our template, rather than the default Barracuda template. Here's how you do this through the object-repository.xml file:

<object class="org.barracudamvc.core.event.events.LongRunningEventGateway">
    <prop name="DEFAULT_TEMPLATE">com.atmr.atmreports.xmlc.LongRunningHTML</prop>
</object>

Now, the LongRunningEventGateway will process this template using a standard BTemplate model; here are the directives available for you to access from your template:

For an example of these, refer to the sample template and see how it uses these directives. Your template will do something very similar.

[Note: if you are using a form with buttons, the way ours does up above, you MUST specify the form to use the POST method.]

Per-Event Configuration

Many times, you will want to configure LongRunning behavior on a per-event basis. For instance, the redirect-on-cancel event and ETA may well vary for each individual event. We have already seen how you can do basic configuration on these settings through the event.xml declaration. 

But what if you need greater granularity? For instance, sometimes you may want configure an event differently based on who the user is, what the request is, etc.

As an example, there are certain times when a LongRunning event may really not take very long to run;  you can tell Barracuda to ignore the fact that the event implements the LongRunning interface by including ApplicationGateway.LR_OVERRIDE_KEY in the event link. For instance, try clicking this link to fire the GetMainScreenSlowly.event while ignoring the fact that it implements the LongRunning interface.

Let's say you actually want to programmatically configure the LongRunning interface? From within your event handler, you can get the LongRunning event simply by casting the event from the context:

//get the LongRunning object so we can update it
LongRunning lr = (LongRunning) context.getEvent();
lr.setRedirectEvent(new GetMainScreen());
lr.setETA(120);

The LongRunning interface provides a number of methods that you might want to use to configure the LongRunning event (ie. to write information _in_ to the LongRunning object):

It also provides a number of other methods, but these are primarily used to LongRunningEventGateway to update the progress screen (ie. it uses these to read information _out_ of the LongRunning object)

How does all this work?

Ok, so the actual mechanics of making all this happen are quite complex. If you really want to understand what is going on, you are going to need to take a close look at the code in ApplicationGateway, LongRunningEventGateway, and the Simple Login example. In the meanwhile, here's a brief overview of the process:

As I said, this is all actually quite complex, because frames effectively turn everything into GET requests. So we need to cache incoming parameters, then reconstitute later. We also need to do quite a few other sophisticated gyrations to make everything work.

One final note: sometimes the user may not press Cancel like they should - instead they simply hit the stop button (case 1), or they simply press CTRL+R on the browser, which kicks off the request again (case 2). In both of these cases, Barracuda tries to catch this event, warn the user they should be pressing Cancel first, and then it actually tries to stop the running request before continuing with the action they invoked (either a stop or a reload). This seems to be working in all major browser, except we are not able to get an event notification for case 2 in IE 6 (which means the request just loads twice).

That about covers it. If you have further questions or comments, email the list!


Put all your content above this linee

$Date: 2006-01-02 15:59:13 -0500 (Mon, 02 Jan 2006) $