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
POE::Component::XUL is an HTTP server that maps all requests to the relevant application instance. It will timeout inactive applications.
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.
POE::XUL::Application
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/.