The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

OpenPlugin - Plugin manager for web applications

SYNOPSIS

  use OpenPlugin();
  my $r = shift;

  my $OP = OpenPlugin->new( config  => { src    => '/etc/myconf.conf' },
                            request => { apache => $r } );

  my $is_authenticated = $OP->authenticate->authenticate({ username => 'badguy',
                                                           password => 'scylla' });
  unless ( $is_authenticated ) {
      $OP->exception->throw( "Login incorrect!" );
  }

  my $session_id = $OP->param->get_incoming('session_id');
  $session = $OP->session->fetch( $session_id );

  $session->{'hair'} = $OP->param->get_incoming('hair');
  $session->{'eyes'} = $OP->param->get_incoming('eyes');
  $OP->session->save( $session );

  $OP->httpheader->send_outgoing();
  print "You have $session->{'hair'} hair and $session->{'eyes'} eyes<br>";

DESCRIPTION

OpenPlugin is an architecture which manages plugins for web applications. It allows you to incorporate any number of plugins and drivers into your web application. For example, the Log plugin has drivers for logging to STDERR, Files, Syslog, Email, and so on. The Session plugin has drivers for storing sessions in Files, Databases, and the like. Changing drivers is easy, you just change the driver name in a config file.

OpenPlugin even has plugins for Params, Cookies, HttpHeaders, and Uploads. Each of these plugins have an Apache and CGI driver. These plugins abstract Apache::Request and CGI.pm, allowing you to build applications that can work seamlessly under mod_perl or CGI. If you want to move your application from one environment to another, you again can just change the driver being used in the config file.

Also in this config file, you can define whether a plugin loads at startup time, or only on demand.

OpenPlugin is designed to be able to handle any number of plugins. Likewise, plugins can have any number of drivers which can manipulate how the plugin functions, or where it can find it's data.

BACKGROUND

Currently, there are a number of web application frameworks available. And while each one is unique, there is a certain amount of functionality that each shares. Often, that functionality is built in to the particular framework, instead of being a seperate component. This means the shared functionality will only benefit from the developers of that one project. Also, if you ever switch frameworks, you may end up with a lot of code that will no longer work.

OpenPlugin offers this functionality that is common between frameworks, but it is designed to be a reusable component. OpenPlugin can be used within any framework or standalone web application. This gives OpenPlugin a unique advantage of being able to grow beyond the abilities of any one developer, and beyond the scope of any one framework.

OpenPlugin has developed into a powerful architecture allowing for extensible applications.

USAGE

There is documentation in the Plugin and Driver files offering specific details on all of it's capabilities. The following is a general overview of how to make use of OpenPlugin.

To use OpenPlugin in your application, first thing you'll do is create a new OpenPlugin object:

 my $OP = OpenPlugin->new( config => { src => /path/to/OpenPlugin.conf } );

OpenPlugin offers a number of plugins and drivers. To make use of any of these, you will use a syntax like:

 $OP->plugin->method();

Some people would rather see:

 $OP->plugin()->method();

It's up to you which to use, they both work. For the examples in this document, I'll tend to use the first example.

So, to retrieve a saved session:

 my $session = $OP->session->fetch( $session_id );

To send an outgoing HTTP header:

 $OP->httpheader->send_outgoing();

Or to authenticate a user:

 my $login = $OP->authenticate->authenticate( 'username', 'password' );

The above syntax assumes that OpenPlugin has some knowledge about what it is we're trying to do. That is, when we attempt to retrieve a session, we never said where to fetch it from, we only told it what session_id we were looking for. And when we authenticate a user, we didn't specify where we are to find the usernames.

This is where the config file comes in. The config file defines all the information about our plugins, and what driver they are to use. Here is an example of how the configuration for the Session plugin might look:

 <plugin session>
     load        = Startup
     expires     = +3h

     <driver ApacheSession>
         Store           = File
         Directory       = /tmp/openplugin
         LockDirectory   = /tmp/openplugin
     </driver>
 </plugin>

This example defines several things for our Session plugin.

First, it tells the plugin to load at the same time OpenPlugin does (startup time). This is particularly useful when running under mod_perl, where you can have OpenPlugin, along with any number of plugins, load at the same time Apache does. You'll find this to be a significant speed increase since Apache is happy to keep these modules compiled and cached within it's memory.

Secondly, we tell Session that it should expire sessions that have been innactive for 3 hours.

Third, we define a driver. We use the ApacheSession driver in this case, and then define a few parameters that this driver needs. We'll store our sessions in files, and those files will be kept in /tmp/openplugin.

So, when we look at our example again:

 my $session = $OP->session->fetch( $session_id );

It makes a bit more sense. The Session plugin has been configured to store sessions in files. We also said that it needs to look in the /tmp/openplugin directory for the session_id that we passed it. Cool!

With sessions, having one driver typically works great. There usually isn't a need to store sessions in more than one place. But what about a plugin like Authenticate? Different applications may desire to keep their usernames in a variety of places. What if one application needs to authenticate users with a Windows NT Server, and another wants to use a UNIX password file? To accomplish this, you can define two drivers:

 <plugin authenticate>
     load       = Startup
     default    = Passwd

     <driver Passwd>
     </driver>

     <driver SMB>
        pdc     = My_NT_Server
        bdc     = My_Backup_Server
        domain  = NT_DOMAIN
     </driver>

 </plugin>

You already know the load parameter, which is telling Authenticate to load whenever OpenPlugin does.

We define two drivers this time, Passwd and SMB (SMB is the protocol Windows NT uses). When using more than one driver, you should tell OpenPlugin which one is your default driver. As you can see above, this example sets Passwd as the default with the statement default = Password.

Having a default driver means that when we say:

 my $login = $OP->authenticate->authenticate( 'username', 'password' );

The authentication is takes place using the UNIX password file, the default.

To authenticate using the SMB driver, we simply would use:

 my $login = $OP->authenticate('SMB')->authenticate( 'username', 'password' );

While the available drivers for each plugin differs, the above syntax remains the same when you want to use multiple drivers. So, if you wanted to be able to cache data in two different places (which of course, uses two different drivers):

 # Cache to the default location
 $OP->cache->save( $data_to_cache );

 # Cache to a DBI compatible database
 $OP->cache('DBI')->save( $data_to_cache );

BUILDING YOUR APPLICATION

As you've begun to see, OpenPlugin makes a lot of functionality available to you. If you are not already using an application framework that helps you organize all of your code, I'd like to recommend that you use the <Application|OpenPlugin::Application> plugin. With all the functionality you'll have at your fingertips, the Application plugin will help keep your code neat and organized.

The Application plugin (also known as OpenPlugin::Application) is a subclass of CGI::Application. It provides the same functionality as CGI::Application, except that you use OpenPlugin instead of CGI.pm. Even though it's a subclass of CGI::Application, CGI.pm will not be loaded. Unless, of course, you are using the CGI driver in OpenPlugin.

OpenPlugin::Application works under the philosophy that web applications can be organized into a series of "run modes". A run mode is typically a single screen of information. OpenPlugin::Application uses this to help you better organize your code, so it's both easier to maintain, and easier to reuse.

While use of this plugin is optional, I find it highly useful, and I definitely recommend it's use. See the documentation for OpenPlugin::Application for more information.

FUNCTIONS

While the main OpenPlugin class does provide some publicaly available functions, you'll find the majority of OpenPlugin's funcionality in it's plugins.

new( %params )

You can pass a number of parameters into the new() method. Each of those parameters can effect how a given plugin, or OpenPlugin as a whole, functions.

The parameters in %params will be available to each plugin as they are loaded.

The syntax for the %params hash is:

 %params = qw(
    plugin_name  => { plugin_param => plugin_value },
    other_plugin => { plugin_param => plugin_value },
 );

For example:

 %params = qw(
    config  => { src    => /path/to/OpenPlugin.conf },
    request => { apache => $r },
 );

There is a special parameter called init. There are certain settings which are in place until a particular plugins are loaded. Lets call that bootstrapping.

You can override these bootstrapping defaults by passing in values to new using the init parameter. The biggest example is logging. The following is the default setup for logging:

        log4perl.rootLogger              = WARN, stderr
        log4perl.appender.stderr         = Log::Dispatch::Screen
        log4perl.appender.stderr.layout  = org.apache.log4j.PatternLayout
        log4perl.appender.stderr.layout.ConversionPattern  = %C (%L) %m%n

You can learn what that all means by reading the Log::Log4perl documentation. But if there's something in that default that you don't like, you may pass in values to override it. Lets say that the default logging level of WARN that it establishes doesn't provide enough information during your debugging session. You can override it like so:

 my $log_info = q(
        log4perl.rootLogger              = INFO, stderr
        log4perl.appender.stderr         = Log::Dispatch::Screen
        log4perl.appender.stderr.layout  = org.apache.log4j.PatternLayout
        log4perl.appender.stderr.layout.ConversionPattern  = %C (%L) %m%n
 );

 my $OP = OpenPlugin->new( init   => { log => $log_info },
                           config => { src => "/path/to/config" },

 );

The new function returns an OpenPlugin object.

state( [ key ], [ value ] )

This function is for storing and retrieving state information. This information is destroyed when the script exits (see the Session and Cache plugins for storing information across requests).

This returns the full state hash if passed no parameters, a value if passed one parameter (a key), or sets a key equal to a given value if sent two parameters.

cleanup()

This function tells the main OpenPlugin module, and all of it's plugins, to perform a "cleanup".

PLUGINS

The API for individual plugins is available by looking at that particular plugin's documentation. The following plugins are available:

DOCUMENTATION

Many of these plugins accept parameters passed into OpenPlugin's new() constructor, and a few even require it. You can obtain a list of what parameters a plugin recognizes by reading the documentation for the plugin and driver which you are using. Generally speaking, the documentation for a plugin shows how to program it's interface, and the documentation for the driver shows how to configure it, along with what parameters you can pass into it. The following sections explain what you can expect to find when reading the documentation for plugins and drivers.

PLUGINS

Each plugin contains documenation on how to use it. It will contain, at a minimum, the following sections:

  • SYNOPSIS

    A brief usage example.

  • DESCRIPTION

    A general description of the module, perhaps containing more examples.

  • METHODS

    The methods you would use to interact with that particular plugin.

DRIVERS

Each driver also contains documentation, describing how you configure or use it. At a minimum, each driver will contain the following sections:

  • PARAMETERS

    Parameters describe information which you would pass in when you instanciate a new OpenPlugin object. Take the following illustration:

     my $OP = OpenPlugin->new( config => { src => "/path/to/OpenPlugin.conf" } );

    In this example, we can see that the src parameter is being passed in as a parameter to the config plugin. These are the sorts of things you can expect to find in the parameters section of any given driver.

  • CONFIG OPTIONS

    This describes options that you can put in the config file for a given driver.

TO DO

OpenPlugin is currently being used in every day production at my workplace. Applications are being based off of it, and they are working great. The OpenThought Application Environment also makes use of it quite successfully.

That being said, OpenPlugin is still under heavy development. There are a lot of things to do.

I consider this alpha software. It by all means has bugs. Parts of the API may change. It's also been known to cause your significant other to think you spend too much time on the computer. Your milage may vary.

The API is not complete. See the TO DO list for the individual plugins to see a few of the ideas that I've written down. Additionally, in order for this to be useful for many people, more drivers need to be created to handle the wide variety of environments OpenPlugin may find itself in.

We also need to create a bunch more documentation, examples, etc.

There is also a need for many more tests.

If you have any further suggestions, I would definitely like to hear them. If you have any patches, I like those too :-)

COPYRIGHT

Copyright (c) 2001-2003 Eric Andreychek. All rights reserved.

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

AUTHORS

Eric Andreychek <eric@openthought.net>

CONTRIBUTORS

Chris Winters initially helped get things rolling. OpenPlugin also makes use of his Class::Factory module, and I occasionally borrow code and ideas from OpenInteract and SPOPS. Thanks Chris!

SEE ALSO

Web applications which make use of OpenPlugin:

1 POD Error

The following errors were encountered while parsing the POD:

Around line 882:

You forgot a '=back' before '=head1'