1POE::Component::PluggabUlsee(r3)Contributed Perl DocumenPtOaEt:i:oCnomponent::Pluggable(3)
2
3
4
6 POE::Component::Pluggable - A base class for creating plugin-enabled
7 POE Components.
8
10 # A simple POE Component that sends ping events to registered sessions
11 # every 30 seconds. A rather convoluted example to be honest.
12
13 {
14 package SimplePoCo;
15
16 use strict;
17 use base qw(POE::Component::Pluggable);
18 use POE;
19 use POE::Component::Pluggable::Constants qw(:ALL);
20
21 sub spawn {
22 my ($package, %opts) = @_;
23 $opts{lc $_} = delete $opts{$_} for keys %opts;
24 my $self = bless \%opts, $package;
25
26 $self->_pluggable_init(prefix => 'simplepoco_');
27 POE::Session->create(
28 object_states => [
29 $self => { shutdown => '_shutdown' },
30 $self => [qw(_send_ping _start register unregister __send_event)],
31 ],
32 heap => $self,
33 );
34
35 return $self;
36 }
37
38 sub shutdown {
39 my ($self) = @_;
40 $poe_kernel->post($self->{session_id}, 'shutdown');
41 }
42
43 sub _pluggable_event {
44 my ($self) = @_;
45 $poe_kernel->post($self->{session_id}, '__send_event', @_);
46 }
47
48 sub _start {
49 my ($kernel,$self) = @_[KERNEL,OBJECT];
50 $self->{session_id} = $_[SESSION]->ID();
51
52 if ($self->{alias}) {
53 $kernel->alias_set($self->{alias});
54 }
55 else {
56 $kernel->refcount_increment($self->{session_id}, __PACKAGE__);
57 }
58
59 $kernel->delay(_send_ping => $self->{time} || 300);
60 return;
61 }
62
63 sub _shutdown {
64 my ($kernel, $self) = @_[KERNEL, OBJECT];
65 $self->_pluggable_destroy();
66 $kernel->alarm_remove_all();
67 $self->alias_remove($_) for $kernel->alias_list();
68 $kernel->refcount_decrement($self->{session_id}, __PACKAGE__) if !$self->{alias};
69 $kernel->refcount_decrement($_, __PACKAGE__) for keys %{ $self->{sessions} };
70
71 return;
72 }
73
74 sub register {
75 my ($kernel, $sender, $self) = @_[KERNEL, SENDER, OBJECT];
76 my $sender_id = $sender->ID();
77 $self->{sessions}->{$sender_id}++;
78
79 if ($self->{sessions}->{$sender_id} == 1) {
80 $kernel->refcount_increment($sender_id, __PACKAGE__);
81 $kernel->yield(__send_event => $self->{_pluggable_prefix} . 'registered', $sender_id);
82 }
83
84 return;
85 }
86
87 sub unregister {
88 my ($kernel, $sender, $self) = @_[KERNEL, SENDER, OBJECT];
89 my $sender_id = $sender->ID();
90 my $record = delete $self->{sessions}->{$sender_id};
91
92 if ($record) {
93 $kernel->refcount_decrement($sender_id, __PACKAGE__);
94 $kernel->yield(__send_event => $self->{_pluggable_prefix} . 'unregistered', $sender_id);
95 }
96
97 return;
98 }
99
100 sub __send_event {
101 my ($kernel, $self, $event, @args) = @_[KERNEL, OBJECT, ARG0..$#_];
102
103 return 1 if $self->_pluggable_process(PING => $event, \(@args)) == PLUGIN_EAT_ALL;
104
105 $kernel->post($_, $event, @args) for keys %{ $self->{sessions} };
106 }
107
108 sub _send_ping {
109 my ($kernel, $self) = @_[KERNEL, OBJECT];
110 my $event = $self->{_pluggable_prefix} . 'ping';
111 my @args = ('Wake up sleepy');
112 $kernel->yield(__send_event => $event, @args);
113 $kernel->delay(_send_ping => $self->{time} || 300);
114
115 return;
116 }
117 }
118
119 use POE;
120
121 my $pluggable = SimplePoCo->spawn(
122 alias => 'pluggable',
123 time => 30,
124 );
125
126 POE::Session->create(
127 package_states => [
128 main => [qw(_start simplepoco_registered simplepoco_ping)],
129 ],
130 );
131
132 $poe_kernel->run();
133
134 sub _start {
135 my ($kernel, $heap) = @_[KERNEL, HEAP];
136 $kernel->post(pluggable => 'register');
137 return;
138 }
139
140 sub simplepoco_registered {
141 print "Yay, we registered\n";
142 return;
143 }
144
145 sub simplepoco_ping {
146 my ($sender, $text) = @_[SENDER, ARG0];
147 print "Got '$text' from ", $sender->ID, "\n";
148 return;
149 }
150
152 POE::Component::Pluggable is a base class for creating plugin enabled
153 POE Components. It is a generic port of POE::Component::IRC's plugin
154 system.
155
156 If your component dispatches events to registered POE sessions, then
157 POE::Component::Pluggable may be a good fit for you.
158
159 Basic use would involve subclassing POE::Component::Pluggable, then
160 overriding "_pluggable_event()" and inserting "_pluggable_process()"
161 wherever you dispatch events from.
162
163 Users of your component can then load plugins using the plugin methods
164 provided to handle events generated by the component.
165
166 You may also use plugin style handlers within your component as
167 "_pluggable_process()" will attempt to process any events with local
168 method calls first. The return value of these handlers has the same
169 significance as the return value of 'normal' plugin handlers.
170
172 Subclassing POE::Component::Pluggable gives your object the following
173 'private' methods:
174
175 "_pluggable_init"
176 This should be called on your object after initialisation, but before
177 you want to start processing plugins. It accepts a number of
178 argument/value pairs:
179
180 'prefix', the prefix for your events (default: 'pluggable_');
181 'reg_prefix', the prefix for the register()/unregister() plugin methods
182 (default: 'plugin_');
183 'types', an arrayref of the types of events that your poco will support,
184 OR a hashref with the event types as keys and their abbrevations
185 (used as plugin event method prefixes) as values;
186
187 Notes: 'prefix' should probably end with a '_'. The types specify the
188 prefixes for plugin handlers. You can specify as many different types
189 as you require.
190
191 "_pluggable_destroy"
192 This should be called from any shutdown handler that your poco has. The
193 method unloads any loaded plugins.
194
195 "_pluggable_process"
196 This should be called before events are dispatched to interested
197 sessions. This gives pluggable a chance to discard events if requested
198 to by a plugin.
199
200 The first argument is a type, as specified to "_pluggable_init()".
201
202 sub _dispatch {
203 # stuff
204
205 return 1 if $self->_pluggable_process($type, $event, \(@args)) == PLUGIN_EAT_ALL;
206
207 # dispatch event to interested sessions.
208 }
209
210 This example demonstrates event arguments being passed as scalar refs
211 to the plugin system. This enables plugins to mangle the arguments if
212 necessary.
213
214 "_pluggable_event"
215 This method should be overridden in your class so that pipeline can
216 dispatch events through your event dispatcher. Pipeline sends a
217 prefixed 'plugin_add' and 'plugin_del' event whenever plugins are added
218 or removed, respectively.
219
220 sub _pluggable_event {
221 my $self = shift;
222 $poe_kernel->post($self->{session_id}, '__send_event', @_);
223 }
224
225 There is an example of this in the SYNOPSIS.
226
228 Subclassing POE::Component::Pluggable gives your object the following
229 public methods:
230
231 "pipeline"
232 Returns the POE::Component::Pluggable::Pipeline object.
233
234 "plugin_add"
235 Accepts two arguments:
236
237 The alias for the plugin
238 The actual plugin object
239
240 The alias is there for the user to refer to it, as it is possible to
241 have multiple plugins of the same kind active in one
242 POE::Component::Pluggable object.
243
244 This method goes through the pipeline's "push()" method, which will
245 call "$plugin-"plugin_register($pluggable)>.
246
247 Returns the number of plugins now in the pipeline if plugin was
248 initialized, "undef"/an empty list if not.
249
250 "plugin_del"
251 Accepts one argument:
252
253 The alias for the plugin or the plugin object itself
254
255 This method goes through the pipeline's "remove()" method, which will
256 call "$plugin-"plugin_unregister($pluggable)>.
257
258 Returns the plugin object if the plugin was removed, "undef"/an empty
259 list if not.
260
261 "plugin_get"
262 Accepts one argument:
263
264 The alias for the plugin
265
266 This method goes through the pipeline's "get()" method.
267
268 Returns the plugin object if it was found, "undef"/an empty list if
269 not.
270
271 "plugin_list"
272 Takes no arguments.
273
274 Returns a hashref of plugin objects, keyed on alias, or an empty list
275 if there are no plugins loaded.
276
277 "plugin_order"
278 Takes no arguments.
279
280 Returns an arrayref of plugin objects, in the order which they are
281 encountered in the pipeline.
282
283 "plugin_register"
284 Accepts the following arguments:
285
286 The plugin object
287 The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
288 The event name[s] to watch
289
290 The event names can be as many as possible, or an arrayref. They
291 correspond to the prefixed events and naturally, arbitrary events too.
292
293 You do not need to supply events with the prefix in front of them, just
294 the names.
295
296 It is possible to register for all events by specifying 'all' as an
297 event.
298
299 Returns 1 if everything checked out fine, "undef"/an empty list if
300 something is seriously wrong.
301
302 "plugin_unregister"
303 Accepts the following arguments:
304
305 The plugin object
306 The type of the hook (the hook types are specified with _pluggable_init()'s 'types')
307 The event name[s] to unwatch
308
309 The event names can be as many as possible, or an arrayref. They
310 correspond to the prefixed events and naturally, arbitrary events too.
311
312 You do not need to supply events with the prefix in front of them, just
313 the names.
314
315 It is possible to register for all events by specifying 'all' as an
316 event.
317
318 Returns 1 if all the event name[s] was unregistered, undef if some was
319 not found.
320
322 The basic anatomy of a pluggable plugin is:
323
324 # Import the constants, of course you could provide your own
325 # constants as long as they map correctly.
326 use POE::Component::Pluggable::Constants qw( :ALL );
327
328 # Our constructor
329 sub new {
330 ...
331 }
332
333 # Required entry point for pluggable plugins
334 sub plugin_register {
335 my($self, $pluggable) = @_;
336
337 # Register events we are interested in
338 $pluggable->plugin_register($self, 'SERVER', qw(something whatever));
339
340 # Return success
341 return 1;
342 }
343
344 # Required exit point for pluggable
345 sub plugin_unregister {
346 my($self, $pluggable) = @_;
347
348 # Pluggable will automatically unregister events for the plugin
349
350 # Do some cleanup...
351
352 # Return success
353 return 1;
354 }
355
356 sub _default {
357 my($self, $pluggable, $event) = splice @_, 0, 3;
358
359 print "Default called for $event\n";
360
361 # Return an exit code
362 return PLUGIN_EAT_NONE;
363 }
364
365 As shown in the example above, a plugin's "_default" subroutine (if
366 present) is called if the plugin receives an event for which it has no
367 handler.
368
369 The special exit code CONSTANTS are documented in
370 POE::Component::Pluggable::Constants. You could provide your own as
371 long as the values match up, though.
372
374 Better documentation >:]
375
377 Chris 'BinGOs' Williams <chris@bingosnet.co.uk>
378
380 Copyright "(c)" Chris Williams, Apocalypse, Hinrik A~Xrn SigurA~Xsson
381 and Jeff Pinyan
382
383 This module may be used, modified, and distributed under the same terms
384 as Perl itself. Please see the license that came with your Perl
385 distribution for details.
386
388 APOCAL for writing the original POE::Component::IRC plugin system.
389
390 japhy for writing POE::Component::IRC::Pipeline which improved on it.
391
392 All the happy chappies who have contributed to POE::Component::IRC over
393 the years (yes, it has been years) refining and tweaking the plugin
394 system.
395
396 The initial idea was heavily borrowed from X-Chat, BIG thanks go out to
397 the genius that came up with the EAT_* system :)
398
400 POE::Component::IRC
401
402 POE::Component::Pluggable::Pipeline
403
404 Both POE::Component::Client::NNTP and POE::Component::Server::NNTP use
405 this module as a base, examination of their source may yield further
406 understanding.
407
408
409
410perl v5.12.0 2009-08-18 POE::Component::Pluggable(3)