File Coverage

blib/lib/OpenPlugin.pm
Criterion Covered Total %
statement 140 174 80.4
branch 26 44 59.0
condition 17 32 53.1
subroutine 29 34 85.2
pod 3 20 15.0
total 215 304 70.7


line stmt bran cond sub pod time code
1             package OpenPlugin;
2              
3 7     7   303649 use strict;
  7         19  
  7         301  
4 7     7   3727 use vars qw( $AUTOLOAD );
  7         22  
  7         503  
5 7     7   5867 use OpenPlugin::Plugin qw();
  7         22  
  7         189  
6 7     7   7543 use OpenPlugin::Utility qw();
  7         19  
  7         172  
7 7     7   45 use Log::Log4perl qw( get_logger );
  7         21  
  7         48  
8              
9 7     7   574 use constant STATE => '_state';
  7         14  
  7         373  
10 7     7   35 use constant TOGGLE => '_toggle';
  7         12  
  7         476  
11 7     7   35 use constant PLUGIN => '_plugin';
  7         12  
  7         306  
12 7     7   37 use constant PLUGINCONF => '_pluginconf';
  7         12  
  7         328  
13 7     7   35 use constant INSTANCE => '_instance';
  7         14  
  7         17639  
14              
15             $OpenPlugin::VERSION = '0.11';
16              
17             # We'll need the logger var throughout this entire file
18             my $logger;
19              
20             sub new {
21 6     6 1 624 my $pkg = shift;
22 6         25 my $params = { @_ };
23 6   33     51 my $class = ref $pkg || $pkg;
24 6         21 my $self = bless( {}, $class );
25              
26 6         43 $self->init( $params->{'init'} );
27              
28             # Save all the parameters which were passed in
29 6         2496 $self->state("command_line", $params );
30              
31             # TODO: We should try and get all this config stuff into the Config plugin
32             # if at all possible, I don't think it belongs here
33              
34             # Read configuration from file if given. Otherwise, see if the package var
35             # has our config in it
36 6   66     53 $params->{'config'}{'src'} ||= $OpenPlugin::Config::Src;
37              
38 6 50 66     97 if ( $params->{'config'}{'src'} or $params->{'config'}{'data'} ) {
39              
40 6         36 $self->load_config( $params );
41              
42             }
43             # Quit if we haven't been given some sort of config to use
44             else {
45 0         0 die "No configuration given! You need to pass in the location ",
46             "to your configuration file, or pass in a hashref containing ",
47             "your configuration data.";
48             }
49              
50 6         53 $self->register_plugins;
51              
52 4         27 return $self;
53             }
54              
55             # Set up some stuff before we begin loading any plugins
56             sub init {
57 6     6 0 24 my ( $self, $params ) = @_;
58              
59 6   50     52 my $log_conf = $params->{'log'} || q(
60             log4perl.rootLogger = WARN, stderr
61             log4perl.appender.stderr = Log::Dispatch::Screen
62             log4perl.appender.stderr.layout = org.apache.log4j.PatternLayout
63             log4perl.appender.stderr.layout.ConversionPattern = %C (%L) %m%n
64             );
65              
66 6         47 Log::Log4perl::init( \$log_conf );
67 6         201148 $logger = get_logger("OpenPlugin");
68              
69             }
70              
71             ########################################
72             # Public methods
73             ########################################
74              
75             # This gets and sets state information for user requests. For instance, we can
76             # maintain the current user and group, whether the user is an administrator,
77             # etc.
78             sub state {
79 152     152 1 309 my ( $self, $key, $value ) = @_;
80              
81              
82             # Just a key passed in, return a single value
83 152 50 66     2231 if( defined $key and not defined $value ) {
    100 66        
84 0         0 $logger->info("Calling state() with key [$key].");
85              
86 0         0 return $self->{ STATE() }{ $key };
87             }
88              
89             # We have a key and value, so assign the value to the key
90             elsif( defined $key and defined $value ) {
91 36         326 $logger->info("Calling state() with key [$key] and value [$value].");
92              
93 36         423 return $self->{ STATE() }{ $key } = $value;
94             }
95              
96             # No key or value, return the entire state hash
97             else {
98 116         405 $logger->info("Calling state() with no parameters.");
99              
100 116         2048 return $self->{ STATE() };
101             }
102             }
103              
104              
105             # Cleans up the current state in this object and sends a message to
106             # all plugins to cleanup their state as well.
107             sub cleanup {
108 0     0 1 0 my ( $self ) = @_;
109              
110 0         0 $logger->info( "Running cleanup()" );
111              
112             # Allow plugins to clean up their own state
113 0         0 foreach my $plugin ( $self->loaded_plugins ) {
114 0         0 $self->$plugin()->cleanup;
115             }
116              
117             # Completely erase all state related information
118 0         0 $self->{ STATE() } = {};
119              
120             # FIXME: This might not be necessary anymore
121             # Recreate a hash key for each plugin
122 0         0 foreach my $plugin ( $self->loaded_plugins ) {
123 0         0 $self->state( $plugin, {} );
124             }
125              
126             }
127              
128             # This should be called before the object is taken out of scope and
129             # should probably incorporated into a DESTROY() method.
130              
131             sub shutdown {
132 0     0 0 0 my ( $self ) = @_;
133 0         0 $logger->info( "Calling shutdown() from OP" );
134 0         0 $self->cleanup();
135              
136             # ... do any additional cleanup so we don't have dangling/circular
137             # references, etc....
138              
139             }
140              
141              
142             ########################################
143             # Accessor methods
144             ########################################
145              
146             # Get a list of all plugins which the config plugin knows about
147             sub get_plugins {
148 31     31 0 74 my ( $self, $plugin ) = @_;
149              
150 31 100       94 if( $plugin ) {
151 25         41 return sort keys %{ $self->config->{'plugin'}{ $plugin }{'plugin'} };
  25         96  
152             }
153             else {
154 6         12 return sort keys %{ $self->config->{'plugin'} };
  6         22  
155             }
156             }
157              
158             # Get a list of all drivers for a given plugin
159             sub get_drivers {
160 27     27 0 48 my ( $self, $plugin ) = @_;
161              
162 27         908 return sort keys %{ $self->{ PLUGINCONF() }{ $plugin }{'driver'} }
  27         163  
163             }
164              
165             # Save any info that we have relating to a plugins configuration
166             sub set_plugin_info {
167 27     27 0 67 my ( $self, $plugin, $nested_plugin ) = @_;
168              
169             # $plugin_info contains all the information about a given plugin that was
170             # found in the configuration file
171 27         42 my $plugin_info;
172 27 100       79 if( $nested_plugin ) {
173 8         24 $plugin_info =
174             $self->config->{'plugin'}{ $plugin }{'plugin'}{ $nested_plugin };
175 8         18 $plugin = $nested_plugin;
176             }
177             else {
178 19         70 $plugin_info = $self->config->{'plugin'}{ $plugin };
179             }
180              
181             # We definitely cannot load a plugin without a driver. Warn and skip if
182             # that is the case.
183 27 50 33     240 unless ( ref $plugin_info eq 'HASH' and $plugin_info->{'driver'} ) {
184 0         0 $logger->warn("Invalid driver listed for [$plugin]: ",
185             "[$plugin_info->{driver}]. Skipping." );
186              
187 0         0 return undef;
188             }
189              
190 27         192 $logger->info( "Driver type found for [$plugin]: ",
191             "[$plugin_info->{driver}]" );
192              
193             # Store this configuration for whenever we need it
194 27         244 return $self->{ PLUGINCONF() }{ $plugin } = $plugin_info;
195              
196             }
197              
198             # Retrieve config information listed about a given plugin
199             sub get_plugin_info {
200 58     58 0 405 my ( $self, $plugin ) = @_;
201              
202 58         410 return $self->{ PLUGINCONF() }{ $plugin };
203             }
204              
205             # Get the name of the class name to use for a given driver
206             sub get_plugin_class {
207 28     28 0 272 my ( $self, $plugin, $driver ) = @_;
208              
209 28 50       73 return undef unless $driver;
210              
211             # Get the class name for the driver, as defined in the drivermap file
212 28         70 my $plugin_class = $self->config->{'drivermap'}{ $plugin }{ $driver };
213              
214 28         148 $logger->info( "Plugin class found for [$plugin]: [$plugin_class]" );
215              
216 28         227 return $plugin_class;
217             }
218              
219              
220             # Retrieve a list of plugins which are currently loaded, return the value we
221             # received when we called it's load() function earlier
222             sub loaded_plugins {
223 0     0 0 0 my $self = shift;
224              
225             # Return an empty list if no plugins are loaded
226 0 0       0 unless ( ref $self->{ PLUGIN() } eq 'HASH' ) {
227 0         0 return ();
228             }
229              
230 0         0 return sort keys %{ $self->{ PLUGIN() } };
  0         0  
231             }
232              
233             # Save the plugin instance (object) that we received by calling its new()
234             # function
235             sub set_plugin_instance {
236 30     30 0 121 my ( $self, $plugin, $driver, $instance ) = @_;
237              
238 30         195 return $self->{ INSTANCE() }{ $plugin }{ $driver } = $instance;
239             }
240              
241             # Set which driver will be used when one isn't explicitely given
242             sub set_default_driver {
243 33     33 0 74 my ( $self, $plugin, $driver ) = @_;
244              
245 33         192 return $self->{ PLUGINCONF() }{ $plugin }{'default'} = $driver;
246             }
247              
248             # Get the name of the default driver for a given plugin
249             sub get_default_driver {
250 155     155 0 218 my ( $self, $plugin ) = @_;
251              
252 155         1117 return $self->{ PLUGINCONF() }{ $plugin }{'default'};
253             }
254              
255              
256             ########################################
257             # Plugin Instanciation
258             ########################################
259              
260             # TODO: I'd like all these functions to be able to handle plugins defined in
261             # the config file to be nested at an arbitrary depth. They currently can only
262             # be two levels deep.
263              
264             # Get a list of plugins, and register them
265             sub register_plugins {
266 6     6 0 16 my $self = shift;
267              
268 6         145 foreach my $plugin ( $self->get_plugins ) {
269 19         85 $self->set_plugin_info( $plugin );
270              
271 19         70 $self->register_plugin( $plugin );
272             }
273              
274             }
275              
276             # Decide how and when to load a plugin
277             sub register_plugin {
278 27     27 0 338 my ( $self, $plugin ) = @_;
279              
280 27         87 my @drivers = $self->get_drivers( $plugin );
281 27         58 my $driver_count = scalar @drivers;
282 27         73 my $default_driver = $self->get_default_driver( $plugin );
283              
284 27         78 foreach my $driver ( @drivers ) {
285              
286 27 50 33     358 if( $driver_count == 1 and not defined $default_driver ) {
287 27         80 $self->set_default_driver( $plugin, $driver );
288             }
289              
290 27         70 my $class_identifier = $plugin . "-" . $driver;
291 27         113 my $class = $self->get_plugin_class( $plugin, $driver );
292              
293             # These plugins have a "load" time of "Startup", meaning they are
294             # loaded when the main OpenPlugin module is
295 27 100       76 if( $self->get_plugin_info( $plugin )->{'load'} eq "Startup" ) {
    50          
296              
297 26 50       1596 unless( grep m/^$class$/,
298             OpenPlugin::Plugin->get_loaded_classes ) {
299              
300             # Tell OpenPlugin::Plugin that we have a new class that we
301             # wish to load now
302 26         1575 OpenPlugin::Plugin->add_factory_type(
303             $class_identifier => $class );
304             }
305              
306 24         868 $self->init_plugin( $plugin, $driver );
307             }
308              
309             # These plugins have a "load" time of "Auto", meaning they are
310             # loaded on demand. If they aren't ever used, they'll never be
311             # loaded
312             elsif ( $self->get_plugin_info( $plugin )->{'load'} eq "Auto" ) {
313              
314 1 50       20 unless( grep m/^$class$/,
315             OpenPlugin::Plugin->get_registered_classes ) {
316              
317             # Tell OpenPlugin::Plugin about a class, so it can load it
318             # if and when we finally decide to use it
319 1         15 OpenPlugin::Plugin->register_factory_type(
320             $class_identifier => $self->get_plugin_class( $plugin,
321             $driver ));
322              
323             }
324             }
325              
326             # We need to know how to load a plugin, it doesn't seem appropriate
327             # to guess. If the configuration isn't correct, give a warning
328             # message, but skip loading it.
329             else {
330 0         0 $logger->warn("Invalid load time listed for [$plugin]: [",
331             $self->{ PLUGINCONF() }{ $plugin }{'load'},
332             "]. Skipping." );
333             }
334             }
335              
336             # Handle plugins defined within other plugins
337 25         161 foreach my $nested_plugin ( $self->get_plugins( $plugin )) {
338 8         31 $self->set_plugin_info( $plugin, $nested_plugin );
339              
340 8         49 $self->register_plugin( $nested_plugin );
341             }
342              
343             }
344              
345             # Make a plugin available to programs using us
346             sub init_plugin {
347 24     24 0 66 my ( $self, $plugin, $driver ) = @_;
348              
349 24 50       122 unless( $plugin ) {
350 0         0 $self->exception->throw( "You must call init_plugin() with a ",
351             "plugin name!" );
352             }
353              
354 24 50       99 unless( $self->get_plugin_info( $plugin )) {
355 0         0 $self->exception->throw( "You attemped to call [$plugin], which is ",
356             "not a valid plugin!" );
357             }
358              
359             # No driver name is okay, we'll just use the default
360 24   33     87 $driver ||= $self->get_default_driver( $plugin );
361              
362             # Create and save an instance for this driver
363 24         101 my $instance = $self->create_plugin_instance( $plugin, $driver );
364 24         173 $self->set_plugin_instance( $plugin, $driver, $instance );
365              
366 24         99 $self->generate_plugin_method_call( $plugin );
367              
368 24         107 $self->{ PLUGIN() }{ $plugin } = $self->$plugin( $driver )->load();
369             }
370              
371             # Create a new instance of a plugin
372             sub create_plugin_instance {
373 24     24 0 57 my ( $self, $plugin, $driver ) = @_;
374              
375 24         68 my $class_identifier = $plugin . "-" . $driver;
376              
377 24         120 return OpenPlugin::Plugin->new( $class_identifier, $self,
378             $self->state->{'command_line'}{ $plugin });
379             }
380              
381             # Build a method call for a given plugin
382             sub generate_plugin_method_call {
383 30     30 0 73 my ( $self, $plugin ) = @_;
384              
385             # Create the new method in the current package's namespace
386 30         91 my $method = __PACKAGE__ . '::' . $plugin;
387 7     7   62 no strict 'refs';
  7         22  
  7         5115  
388              
389             # Don't redefine existing method names. This will both save us time, and
390             # is more secure.
391 30 50       58 unless ( defined &{ $method } ) {
  30         293  
392              
393 30         181 $logger->info("Generating method [$method]");
394              
395 30         1465 *{ $method } =
396             sub {
397 152     152   12634 my ( $self, $driver ) = @_;
398 152   66     663 $driver ||= $self->get_default_driver( $plugin );
399              
400 152 50       899 unless( $driver ) {
401 0         0 $self->exception->throw( "No driver found for [$plugin].",
402             "If you have multiple drivers ",
403             "defined, you must assign one ",
404             "as the default." );
405             }
406              
407             # If there is already an instance for this particular driver,
408             # return it
409 152 50       576 if( $self->{ INSTANCE() }{ $plugin }{ $driver } ) {
410 152         1291 return $self->{ INSTANCE() }{ $plugin }{ $driver };
411             }
412              
413             # If there isn't yet an instance (typical when the plugin isn't
414             # loaded at startup), create one and return it
415             else {
416 0         0 my $instance = $self->create_plugin_instance( $plugin,
417             $driver );
418              
419 0         0 return $self->set_plugin_instance( $plugin, $driver,
420             $instance );
421             }
422             }
423 30         1733 }
424             }
425              
426              
427             ########################################
428             # AUTOLOAD
429             # (so great it gets its own section!)
430             ########################################
431              
432             sub AUTOLOAD {
433 0     0   0 my ( $self, $driver ) = @_;
434 0         0 my $request = $AUTOLOAD;
435              
436 0         0 $request =~ s/.*://;
437              
438 0         0 $logger->info( "Autoload request: [$request]\n" );
439 0         0 $self->init_plugin( $request, $driver );
440 0         0 $self->$request( $driver );
441             }
442              
443              
444             # Lets not go looking for DESTROY via AUTOLOAD
445 0     0   0 sub DESTROY { }
446              
447              
448             ########################################
449             # CONFIGURATION
450             ########################################
451              
452             # Configuration is different from other plugins because of the bootstrapping
453             # issue.
454             sub load_config {
455 6     6 0 15 my ( $self, $params ) = @_;
456              
457 6 50       98 unless( grep m/^OpenPlugin::Config$/,
458             OpenPlugin::Plugin->get_loaded_classes ) {
459              
460 6         110 OpenPlugin::Plugin->add_factory_type(
461             "config" => 'OpenPlugin::Config' );
462             }
463              
464 6         142 my $config = OpenPlugin::Plugin->new( 'config', $self,
465             $params->{'config'} );
466              
467 6 100       63 if( $params->{'config'}{'src'} ) {
    50          
468 1         20 $self->set_plugin_instance( "config", 'built-in', $config->read );
469             }
470             elsif( $params->{'config'}{'data'} ) {
471 5         28 $self->set_plugin_instance( "config", 'built-in',
472             $config->read( $params->{'config'}{'data'} ));
473             }
474              
475 6         67 $self->generate_plugin_method_call( "config" );
476 6         33 $self->set_default_driver( "config", "built-in" );
477              
478 6 50 66     102 if( $params->{'config'}{'data'} and $params->{'config'}{'src'} ) {
479 0           $self->config->read( $params->{'config'}{'data'} );
480             }
481              
482             }
483              
484              
485              
486             1;
487              
488             __END__