File Coverage

blib/lib/POE/XUL/Controler.pm
Criterion Covered Total %
statement 33 175 18.8
branch 0 36 0.0
condition n/a
subroutine 11 34 32.3
pod 0 18 0.0
total 44 263 16.7


line stmt bran cond sub pod time code
1             package POE::XUL::Controler;
2             # $Id: Controler.pm 1566 2010-11-03 03:13:32Z fil $
3             #
4             # Copyright Philip Gwyn / Awalé 2007-2010. All rights reserved.
5             #
6              
7 14     14   53 use strict;
  14         20  
  14         343  
8 14     14   49 use warnings;
  14         18  
  14         350  
9              
10 14     14   47 use Carp;
  14         17  
  14         688  
11 14     14   52 use Digest::MD5 qw(md5_hex);
  14         19  
  14         548  
12 14     14   51 use POE::Kernel;
  14         23  
  14         85  
13 14     14   6642 use POE::XUL::ChangeManager;
  14         29  
  14         400  
14 14     14   5868 use POE::XUL::Event;
  14         25  
  14         343  
15 14     14   64 use POE::XUL::Logging;
  14         17  
  14         771  
16 14     14   55 use Scalar::Util qw( weaken );
  14         13  
  14         474  
17              
18 14     14   47 use constant DEBUG => 0;
  14         15  
  14         17031  
19              
20             our $VERSION = '0.0601';
21              
22             ##############################################################
23             sub new
24             {
25 6     6 0 8 my( $package, $timeout, $apps ) = @_;
26 6         23 my $self = bless {
27             sessions => {},
28             timeout => $timeout,
29             apps => $apps,
30             events => {}
31             }, $package;
32 6         20 return $self;
33             }
34              
35             ##############################################################
36             sub build_event
37             {
38 0     0 0   my( $self, $event_name, $CM, $resp, $req ) = @_;
39 0           my $event = POE::XUL::Event->new( $event_name, $CM, $resp );
40              
41             # Keep a weak reference so we can cancel the event if needs be
42 0           my $r = 0+$req;
43 0           $self->{events}{$r} = $event;
44 0           weaken( $self->{events}{$r} );
45 0           DEBUG and xwarn "BUILD r=$r event=$event";
46              
47 0           return $event;
48             }
49              
50             ##############################################################
51             sub build_change_manager
52             {
53 0     0 0   my( $self ) = @_;
54              
55 0           return POE::XUL::ChangeManager->new();
56             }
57              
58              
59              
60              
61             ##############################################################
62             # Does a given session ID exist?
63             sub exists
64             {
65 0     0 0   my( $self, $SID ) = @_;
66 0           return exists $self->{sessions}{ $SID };
67             }
68              
69             ##############################################################
70             # How many sessions currently exist
71             sub count
72             {
73 0     0 0   my( $self ) = @_;
74 0           return 0 + keys %{ $self->{sessions} };
  0            
75             }
76              
77              
78             ##############################################################
79             # A new session has been created
80             sub register
81             {
82 0     0 0   my( $self, $SID, $session, $CM ) = @_;
83              
84 0           DEBUG and xdebug "register SID=$SID";
85             # TODO make sure the session has the SID as an alias?
86 0           $self->{sessions}{ $SID } = {
87             session => $session->ID,
88             CM => $CM
89             };
90 0           $self->keepalive( $SID );
91             }
92              
93             ##############################################################
94             # A session has been shutdown
95             sub unregister
96             {
97 0     0 0   my( $self, $SID ) = @_;
98 0 0         return unless $self->{sessions}{ $SID };
99 0           DEBUG and xdebug "Unregister SID=$SID";
100 0           my $details = delete $self->{sessions}{ $SID };
101              
102 0           my $tid = $details->{timeout_id};
103 0           my $session = $details->{session};
104 0           my $CM = $details->{CM};
105 0 0         if( $tid ) {
106 0           $poe_kernel->alarm_remove( $tid );
107             }
108 0           $CM->dispose;
109 0           $poe_kernel->post( $session, 'shutdown', $SID ); # TODO use alias $SID
110             }
111              
112             ##############################################################
113             sub keepalive
114             {
115 0     0 0   my( $self, $SID ) = @_;
116 0 0         return unless $self->{sessions}{ $SID };
117 0           my $tid = $self->{sessions}{$SID}{timeout_id};
118 0 0         if( $tid ) {
119            
120 0           $poe_kernel->delay_adjust( $tid, $self->{timeout} );
121 0           DEBUG and
122             xdebug "timeout for $SID: tid=$self->{sessions}{$SID}{timeout_id} timeout=$self->{timeout}";
123             }
124             else {
125             # session_timeout is defined in POE::Component::XUL
126             $self->{sessions}{ $SID }{timeout_id} =
127             $poe_kernel->delay_set( 'session_timeout',
128             $self->{timeout},
129 0           $SID );
130 0           DEBUG and
131             xdebug "timeout for $SID: tid=$self->{sessions}{$SID}{timeout_id} timeout=$self->{timeout}";
132             }
133             }
134              
135              
136             ##############################################################
137             # Find the constructor for a package
138             sub package_ctor
139             {
140 0     0 0   my( $self, $package ) = @_;
141            
142 0 0         confess "No package" unless $package;
143 0           return $package->can( 'spawn' );
144             }
145              
146             ##############################################################
147             # Spawn a component from a package
148             sub package_build
149             {
150 0     0 0   my( $self, $package ) = @_;
151 0           my $ctor = $self->package_ctor( $package );
152 0 0         unless( $ctor ) {
153             return sub {
154 0     0     my( $event ) = @_;
155 0           $event->response->content( "Can't build an application from $package" );
156 0           $event->response->code( 500 );
157 0           };
158             }
159 0     0     return sub { $ctor->( $package, @_ ) };
  0            
160             }
161              
162              
163             ##############################################################
164             sub boot
165             {
166 0     0 0   my( $self, $req, $resp ) = @_;
167 0           my $app = $req->param( 'app' );
168 0 0         unless( $app ) {
169 0           xlog "Controler: Request must have application name";
170 0           return "Controler: Request must have application name";
171             }
172 0           my $A = $self->{apps}{$app};
173              
174 0 0         unless( $A ) {
175 0           xlog "Unknown application: $app";
176 0           return "Application inconnue : $app";
177             }
178              
179 0 0         unless( ref $A ) {
180 0           $A = $self->package_build( $A );
181             }
182              
183             # use Data::Dumper;
184             # xlog "A=", Dumper $A;
185 0           my $CM = $self->build_change_manager();
186              
187 0           my $event = $self->build_event( 'boot', $CM, $resp, $req );
188 0           $event->__init( $req );
189             $event->coderef(
190             sub {
191 0     0     my( $event ) = @_;
192 0           my $SID = $self->make_session_id;
193 0           $event->SID( $SID );
194 0           $event->CM->SID( $SID );
195 0           my $session = $A->( $event, $app );
196 0           $self->register( $SID, $session, $event->CM );
197 0           $event->defer;
198 0           $poe_kernel->post( $SID, 'boot', $event, $app );
199             }
200 0           );
201              
202 0           $self->xul_request( $event );
203 0           return;
204             }
205              
206             ##############################################################
207             sub close
208             {
209 0     0 0   my( $self, $SID, $req, $resp ) = @_;
210              
211 0           my $S = $self->{sessions}{ $SID };
212 0 0         die "Can't find session $SID" unless $SID;
213              
214 0           my $event = $self->build_event( 'close', $S->{CM}, $resp, $req );
215             $event->coderef(
216             sub {
217 0     0     xlog "Close $SID";
218             # TODO : use alias $SID
219 0           my $session = $poe_kernel->ID_id_to_session( $S->{session} );
220 0           $poe_kernel->signal( $session, 'UIDESTROY' );
221 0           $self->unregister( $SID );
222             }
223 0           );
224              
225 0           $self->xul_request( $event );
226 0           return;
227             }
228              
229             ##############################################################
230             sub connect
231             {
232 0     0 0   my( $self, $SID, $req, $resp ) = @_;
233              
234 0           my $S = $self->{sessions}{ $SID };
235 0 0         die "Can't find session $SID" unless $SID;
236              
237 0           my $event = $self->build_event( 'connect', $S->{CM}, $resp, $req );
238 0           $event->__init( $req );
239              
240             $event->coderef( sub {
241 0     0     $event->defer;
242 0           $poe_kernel->post( $SID, 'connect', $event );
243 0           } );
244 0           $self->xul_request( $event );
245 0           return;
246             }
247              
248             ##############################################################
249             sub disconnect
250             {
251 0     0 0   my( $self, $SID, $req, $resp ) = @_;
252              
253 0           my $S = $self->{sessions}{ $SID };
254 0 0         die "Can't find session $SID" unless $SID;
255              
256 0           my $event = $self->build_event( 'disconnect', $S->{CM}, $resp, $req );
257 0           $event->__init( $req );
258              
259             $event->coderef( sub {
260 0     0     $event->defer;
261 0           $poe_kernel->post( $SID, 'disconnect', $event );
262 0           } );
263 0           $self->xul_request( $event );
264 0           return;
265             }
266              
267             ##############################################################
268             sub request
269             {
270 0     0 0   my ( $self, $SID, $event_type, $req, $resp ) = @_;
271              
272 0           my $S = $self->{sessions}{ $SID };
273 0 0         die "Can't find session $SID" unless $SID;
274              
275 0           my $event = $self->build_event( $event_type, $S->{CM}, $resp, $req );
276 0           $event->__init( $req );
277              
278 0           $self->xul_request( $event );
279             }
280              
281              
282             ##############################################################
283             # Standard XUL request (Click / Change / etc )
284             sub xul_request
285             {
286 0     0 0   my( $self, $event ) = @_;
287              
288 0           $event->done( 1 );
289 0           $event->run();
290              
291 0           DEBUG and xdebug "Request done";
292 0 0         if( $event->is_flushed ) {
    0          
293             # User code might have already flushed everything
294 0           DEBUG and xdebug "Request already flushed";
295             }
296             elsif( $event->done ) {
297 0           DEBUG and xdebug "Response now";
298 0           $event->flush;
299             }
300             else {
301             # User code wants us to wait
302 0           DEBUG and xdebug "Defered response";
303             # User code will then call $event->finish when the time is right
304             }
305 0           return 1;
306             }
307              
308             ##############################################################
309             ## Cancel a request. This happens on browser disconnect
310             sub cancel
311             {
312 0     0 0   my( $self, $request ) = @_;
313              
314 0           my $r = 0+$request;
315 0           my $event = $self->{events}{ $r };
316 0 0         unless( $event ) {
317 0 0         if( $request->method ne 'DISCONNECT' ) {
318 0           xlog "FAILURE! I no longer have an event for $request ", 0+$request;
319             }
320 0           return;
321             }
322              
323 0           my $SID = $event->SID;
324 0           my $S = $self->{sessions}{ $SID };
325 0 0         unless( $SID ) {
326 0           xwarn "Can't find session $SID";
327 0           return;
328             }
329              
330             DEBUG and
331 0           xwarn "CANCEL r=$r event=$event SID=$SID";
332            
333 0           $event->cancel;
334             # TODO : do I need to set a flag on the CM?
335             }
336              
337             ##############################################################
338             ## Generate an unguessable session ID.
339             ## Though unguessable isn't all that useful : it can be sniffed off the air
340             sub make_session_id {
341 0     0 0   my $self = shift;
342 0           my $id = md5_hex($$, time, rand(9999));
343             # Format it like a UUID: B6ED3B3F-72C8-3EEF-8173-EC86AA01EA29
344 0           substr( $id, 20, 0, '-' );
345 0           substr( $id, 16, 0, '-' );
346 0           substr( $id, 12, 0, '-' );
347 0           substr( $id, 8, 0, '-' );
348 0           return $id;
349             }
350              
351             1;
352