NAME

POE::XUL - Framework for remote XUL application in POE

SYNOPSIS

use POE;
use POE::Component::XUL;

POE::Component::XUL->spawn( { apps => {   
                                    Test => 'My::App',
                                    # ....
                            } } );
$poe_kernel->run();

##########
package My::App;
use POE;
use POE::XUL::Node;
use base qw( POE::XUL::Application );

#####
sub boot {
    my( $self, $event ) = @_;
    $self->{D} = Description( "do the following" );
    Boot( "This is a test application" );
    Window( HBox( $self->{D}, 
                  Button( label => "click me", 
                          Click => 'Click' ) ) );
    $self->createHandler( 'other_state' );
}

#####
sub Click {
    my( $self, $event ) = @_;
    $event->defer;
    $poe_kernel->yield( 'other_state', $event );
}

sub other_state {
    my( $self, $event ) = @_;
    $self->{D}->textNode( 'You did it!' );
    $self->{W}->firstChild->appendChild( $self->{B2} );
    $event->handled;
}

#####
sub shutdown {
    my( $self, $SID ) = @_;
    $kernel->alias_remove( $self->{SID} );
}

See also the examples in eg/.

DESCRIPTION

POE::XUL is a framework for creating remote XUL applications with POE. It includes a web server, a Javascript client library for Firefox and a widget toolkit in Perl.

POE::XUL is pronounced similar to puzzle.

At the heart of POE::XUL is the concept of mirror objects. That is, each XUL element exists as a Perl object (POE::XUL::Node) in the server and as a DOM object in the client. A ChangeManager on the server and the javascript client library are responsible for keeping the objects in sync. Note that while all element attribute changes in the server are mirrored in the client, only the most important attributes (value, selected, ...) are mirrored from the client to the server.

POE::XUL currently uses a syncronous, event-based model for updates. This will be changed to an asyncronous, bidirectional model (comet) soon, I hope.

XUL is only supported by browsers from the mozilla project (Firefox and xulrunner). While this limits POE::XUL's use for general web application, POE::XUL would make for some very powerful intranet apps.

NOTE: POE::XUL should be considered alpha quality. While I have apps based on POE::XUL in production, the documentation is probably incomplete and this API will probably change.

POE::XUL is a fork of Ran Eilam's XUL::Node. POE::XUL permits the async use of POE events during event handling and multiple window. It also removes the use of the excesively slow Aspect and the heavy XML wire protocol. POE::XUL::Node's API is closer to that of a DOM element. XUL::Node's (IMHO) dangerous autoloading of XUL::Node::Application packages has been removed.

Application server

<<<<<<< .working POE::XUL applications generaly have one POE::session per application instance. This POE session is spawned when a boot request is recieved from the client. The session then must handle a 'boot' event, where-in it creates a Window element and its children. The application session is kept active, handling the user events it has defined, until the users stops using it, that is a period of inactivity. The session is then sent a 'timeout' event followed by a 'shutdown' event. ======= POE::Component::XUL is an HTTP server that maps all requests to the relevant application instance. It will timeout inactive applications. >>>>>>> .merge-right.r1005

<<<<<<< .working Because every application stays in-memory for the entire duration of the application, you will probably want to set up a HTTP proxy front-end with process affinity. Or be very sure that no POE state blocks. ======= An application instance stays in-memory for the entire duration of the application. There is no saving and loading of the application data for each HTTP request. Because of this, you will probably want to set up a HTTP proxy front-end with process affinity. Or be very sure that no POE state blocks. >>>>>>> .merge-right.r1005

<<<<<<< .working It might also be possible to have multiple POE::XUL applications within one session. Tests needed. ======= >>>>>>> .merge-right.r1005

<<<<<<< .working =head2 XUL elements ======= =head2 POE::XUL::Application >>>>>>> .merge-right.r1005

POE::XUL applications are a sub-class of POE::XUL::Application, which takes care of most of the interaction with the server and provides many convienient features to the application.

It is also possible to write a POE::XUL application in pure-POE. See POE::XUL::POE.

XUL elements

If you are not familiar with XUL, you should read http://www.xulplanet.com/tutorials/xultu/intro.html. You should also keep http://developer.mozilla.org/en/docs/XUL handy.

XUL nodes are created and manipulated with POE::XUL::Node. Each application must create a Window node and all its children.

The change manager

The change manager is an object that keeps the XUL elements in the browser and in the application server in sync. See POE::XUL::ChangeManager. Each application instance has a single change manager, which exists for the duration of the instance.

The change manager isn't directly available to user applications. They interact with the change manager via POE::XUL::Node. Keeping the change manager visible to POE::XUL::Node is the job of POE::XUL::Session or "wrap" in POE::XUL::Event.

More details

There are many layers POE::XUL. Maybe too many.

First off, the browser or xulrunner loads start.xul?AppName, which loads the Javascript client library and any necessary CSS. The client library sends a boot event to the server using prototype.js. POE::Component::XUL handles HTTP requests in the server. For a boot request, it creates a POE::XUL::ChangeManager for the application which is used by the event to capture any changes to POE::XUL::Node. The controler then spawns the application and calls its boot state. All nodes created during the boot request will have been noticed by the change manager. These nodes are converted into JSON instructions by the ChangeManager, which are sent as the HTTP response. The JS client library decodes the JSON instructions, populating the XUL DOM tree with the new nodes.

The user then interacts with the XUL elements, which will provoke DOM events. These events are turned into an AJAX request by the JS client library. POE::Component::XUL decodes these requests and hands them to the POE::XUL::Controler. The Controler creates and populates an POE::XUL::Event. The Event will get the change manager to handle any event side-effects, such as setting value of the target node. The Event will then call any user-defined event listeners. When the event is finished, the change manager converts any changes to the POE::XUL::Nodes to JSON instructions, which are sent as the HTTP response. The JS client library decodes the JSON instructions, modifying the XUL DOM tree as necessary.

Understand? Maybe the following diagram will help:

                                    User
                                     |
Firefox or xulrunner              DOM Node
                                     |
                              +------+------+
                             /               \
JS client library          Event           Response
                            \/                /\
HTTP/AJAX                 Request            JSON
                            \/                /\
POE::Component::XUL       decode              ||
POE::XUL::Controler       create Event        ||
POE::XUL::Event           side effects       flush
POE::XUL::ChangeManger    record changes -> convert

XBL

You are encouraged to create your own XUL nodes with XBL. To do so, you will need a custom start.xul that loads the CSS that defines your XBL. To create the nodes with POE::XUL::Node

POE::XUL EVENTS

The life of an application is controled by 1 package method and 2 or more POE events.

spawn

Not actually an event! This is a package method that will be called to create a new application instance. See "spawn" in POE::XUL::Application.

boot

Once the application's session has been spawned, a boot event is sent. This event must create at least Window with POE::XUL::Node. It should also create all necessary child nodes.

See "boot" in POE::XUL::Application and "boot" in POE::XUL::Event.

timeout

Called after the application has been inactive (no events from the client) for longer then the timeout value. No action is required.

See "timeout" in POE::XUL::Event.

shutdown

Posted when it is time to delete an application instance. This is either when the instance has timed-out, or when the server is shutting down.

See "shutdown" in POE::XUL::Application, "shutdown" in POE::XUL::POE and "shutdown" in POE::XUL::Event.

MULTIPLE WINDOWS

POE::XUL applications may have multiple windows open at once. These are main window (created by "boot") and multple sub-windows.

A sub-window is created with the "open" in POE::XUL::Window method.

When the browser creates the window a connect event is sent. When the window is closed disconnect event is sent.

See also "connect" in POE::XUL::POE and "disconnect" in POE::XUL::POE.

DOM EVENTS

After the boot event, further interaction happens via callback events that you defined on your nodes. A callback may be a coderef or a POE event.

Note that POE::XUL events to not bubble like DOM events do.

Click

The most important event. Happens when a user clicks on a button. The application will react accordingly. See "Click" in POE::XUL::Event for more details.

Change

A less important event, Change is called when the value of a TextBox has changed. The application does not have to update the source node's value; this is a side-effect handled by the ChangeManager. See "Change" in POE::XUL::Event for more details.

Select

See "Select" in POE::XUL::Event for more details.

Pick

Called when the users selects a colour in a Colorpicker, Datepicker or other nodes. See "Pick" in POE::XUL::Event for more details.

ARCHITECTURE

There are many layers POE::XUL. Maybe too many.

First off, the browser or xulrunner loads start.xul?AppName, which loads the Javascript client library and any necessary CSS. The client library sends a boot event to the server using prototype.js. POE::Component::XUL handles HTTP requests in the server. For a boot request, it creates a POE::XUL::ChangeManager for the application which is used by the event to capture any changes to POE::XUL::Node. The controler then spawns the application and calls its boot state. All nodes created during the boot request will have been noticed by the change manager. These nodes are converted into JSON instructions by the ChangeManager, which are sent as the HTTP response. The JS client library decodes the JSON instructions, populating the XUL DOM tree with the new nodes.

The user then interacts with the XUL elements, which will provoke DOM events. These events are turned into an AJAX request by the JS client library. POE::Component::XUL decodes these requests and hands them to the POE::XUL::Controler. The Controler creates and populates an POE::XUL::Event. The Event will get the change manager to handle any event side-effects, such as setting value of the target node. The Event will then call any user-defined event listeners.

In the case of POE::XUL::Application, POE::XUL::Session will handle furthur side effects of the event, then call your application's handlers, if any are defined.

After the event has been handled, the change manager converts any changes to the POE::XUL::Nodes to JSON instructions, which are sent as the HTTP response. The JS client library decodes the JSON instructions, modifying the XUL DOM tree as necessary.

Understand? Maybe the following diagram will help:

                                    User
                                     |
Firefox or xulrunner              DOM Node
                                     |
                              +------+------+
                             /               \
JS client library          Event           Response
                            \/                /\
HTTP/AJAX                 Request            JSON
                            \/                /\
POE::Component::XUL       decode              ||
POE::XUL::Controler       create Event        ||
POE::XUL::Event           side effects      handled
POE::XUL::Session         _invoke_state       ||
POE::XUL::Application     event handlers      ||
POE::XUL::ChangeManger    record changes -> convert

TODO

POE::XUL is still a work in progress. Things that aren't done:

Keepalive

If a keepalive request was sent every X seconds, the application timeout could be much shorter, as we would know sooner a browser window was closed. This would allow us to recover the memory sooner.

Comet

Move from a synchronous event-based model to a full, bi-directional, asynchronous model using Comet (http://cometd.com/). Comet would also act as a keepalive.

Better XUL coverage

There are no tests for <colorpicker>, <datepicker>, <toolbar<gt>, <listbox<gt>, <tab<gt> and more.

AUTHOR

Philip Gwyn <gwyn-at-cpan.org>

CREDITS

Based on XUL::Node by Ran Eilam, POE::Component::XUL by David Davis, and of course, POE, by the illustrious Rocco Caputo.

COPYRIGHT AND LICENSE

Copyright 2007 by Philip Gwyn. All rights reserved;

Copyright 2005 by David Davis and Teknikill Software;

Copyright 2003-2004 Ran Eilam. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

perl(1), POE::XUL::Node, POE::XUL::Event, POE::XUL::Controler, POE::XUL::Application, http://www.prototypejs.org/.