cvsuser     02/02/05 14:26:44

  Added:       P5EEx/Blue/P5EEx/Blue devguide.pod
  Log:
  began the devguide to describe the flow of control through the framework
  
  Revision  Changes    Path
  1.1                  p5ee/P5EEx/Blue/P5EEx/Blue/devguide.pod
  
  Index: devguide.pod
  ===================================================================
  #!perl -w
  # run this document through perl to check its syntax
  use Pod::Checker;
  podchecker(\*DATA);
  __END__
  
  =head1 NAME
  
  P5EEx::Blue::devguide - P5EE Developer's Guide
  
  =head1 INTRODUCTION
  
  This is the Developer's Guide to the
  P5EE is the Perl 5 Enterprise Environment.
  You can find out more background to the project on the web.
  
    http://p5ee.perl.org
    http://www.officevision.com/pub/p5ee
  
  =head1 P5EE DESIGN PHILOSOPHY
  
  When the P5EE project was begun, there were already
  
   * many outstanding Perl packages on CPAN
   * an excellent systems architecture for Perl webapps (mod_perl)
   * several excellent web application development frameworks
  
  So why was P5EE needed?
  
  For a variety of reasons, there was never sufficient unity of
  purpose or direction within the Perl community to provide
  a coherent blueprint of what Enterprise Programming in Perl is
  or how to do it.
  
   * What are the pieces?
   * What pieces are default? standard? mandatory? optional?
   * How do they fit together?
   * How can I override or customize them?
   * What techniques do I need to use to assemble them effectively?
  
  After "Enterprise Systems" were defined (see website), the field
  of existing frameworks, solutions, and components was surveyed.
  The goal was to examine everything that people were already doing,
  divide it into pieces that seemed interchangeable, and come up
  with a unified blueprint.  Pieces that people often like to do
  differently (template systems, persistence frameworks, configuration
  files) were allowed to vary, while their essential contribution
  to the working system was standardized in the framework.
  
  It should be noted that most of the work on what people might
  term "Enterprise Systems" in Perl was actually focused on
  "Web Systems" in Perl with a relational database.
  
  The goal of the P5EE design is to unify the Perl community
  on a framework for cooperation, while providing flexibility in
  the areas that might otherwise divide it.
  
  Essentially, everything in P5EE is overridable and customizable,
  but good defaults are provided for everything as well.
  
  On the practical side, P5EE was also designed to allow for gradual
  adoption and incorporation into existing projects.
  
  =head1 P5EE Execution Flow: CGI
  
  The first step to understanding the flow of execution through
  the P5EE framework is to understand the flow in the CGI
  Context.
  
  This is one of the most challenging contexts
  to develop for because of the stateless nature of HTTP,
  the need to initialize all resources before accessing them,
  and the need to properly shut down all resources after using
  them or in case of user abort.
  
  =head2 cgi-bin/p5ee and the Initialization Config File
  
  All usage of P5EE from the web can be driven through the CGI program,
  "cgi-bin/p5ee".  (Actually, depending on the settings in the Initialization
  Config File, the "p5ee" program might not be a CGI program at all.)
  The p5ee program should be installed at a location which is executable
  as a CGI program such as the following.
  
    http://www.officevision.com/cgi-bin/pub/p5ee/p5ee
  
  The "p5ee" progam runs with the "-wT" switches turned on for maximum
  safety, security, and enforcement of programming rigor.
  
  Then a BEGIN block is executed to read the Initialization Config File
  to get low level config settings and perhaps modify the @INC variable.
  Modification of the @INC variable in the BEGIN block through configuration
  is critical so that you can have multiple versions of modules installed
  (at various stages of development through testing, production, and support).
  and access the correct ones.
  
  The Initialization Config File (.conf) is located in the following way.
  First, the PATH_INFO is checked and a corresponding .conf file is opened.
  Thus, the following URL
  
    http://www.officevision.com/cgi-bin/pub/p5ee/p5ee/ecommerce/shop
  
  would open "ecommerce_shop.conf" in the directory of the "p5ee" program. 
  
  If it is not found, "$0.conf" is opened.  That would be "p5ee.conf"
  in this case.  However, you can see that this allows for the "p5ee"
  program to be renamed, and (according to its configuration) it will
  behave like a completely different application.
  
  If it is still not found, "p5ee.conf" is opened.
  If this is not found, no Initialization Config information will be
  used and all defaults will be used.
  
  Whichever .conf file was first opened, it is read for simple configuration
  variables of the form "variable = value".  Anything following a "#" is considered
  a comment.  Leading and trailing spaces are removed, and blank lines are
  ignored.  Spaces may precede or follow the "=" sign without affecting the
  "variable" or the "value".  The "variable" must be a sequence of alphabetic 
characters
  and ".", "_", or "-" (i.e. matches /[a-zA-Z_.-]+/).  The value may be
  any string of characters (including none), but leading and trailing spaces
  are stripped.  The variable/value pairs are saved in a global hash in %main::conf.
  
  If the "perlinc" variable is set, it is understood to be a comma-separated
  list of directories to search for Perl modules.  This list is placed
  at the beginning of the special Perl @INC variable.
  
  Some sample lines in the .conf file are:
  
    perlinc = /usr/ov/acoc/dev/src/P5EEx-Blue, /usr/ov/acoc/dev/lib/perl5/5.6.0
    debugmode = record
    showsession = 1
    gzip = 1
    configFile = p5ee.pl
  
  The meanings of these variables are:
  
    perlinc      - directories to search for perl modules
    debugmode    - (off|record|replay) useful for recording a failed CGI request and 
replaying it for debugging
    debug        - 
(0|1,P5EEx::Blue::Context::CGI|6,P5EEx::Blue::Repository::DBI.select_rows)
    showsession  - (0|1) show the contents of the session in an HTML comment
    gzip         - (0|1) allows compression of HTML output if the client browser 
supports it
  
  Additional variables may be provided.  However, suitable defaults
  are usually detected if they are not supplied.
  
    contextClass - (i.e. P5EEx::Blue::Context::CGI) class for the context
    configClass  - (i.e. P5EEx::Blue::Config::File) class for the config
    configFile   - (i.e. "config.pl") name of the main configuration file
    configSerializerClass - (i.e. P5EEx::Blue::Serializer::Dumper) class for the 
config deserialization
    sessionClass - (i.e. P5EEx::Blue::Session::HTMLHidden) class for the session
    defaultWname - widget name to be the first current_widget to be displayed
  
  After the Initialization Config File is read into %main::conf, the
  command line arguments are scanned for options of the form "-variable" or
  "-variable=value".  (Options may also start with double-dashes, "--".)
  Such options are removed from the command line, and the variable/value
  pair is added to the %main::conf hash, thus overriding any values from
  the Initialization Config File.  The CGI environment never passes options
  to the program in this way (with a preceding dash), so this is mainly
  useful for debugging at the command line
  (i.e. "p5ee -debugmode=replay -debug=1 -gzip=0").
  
  =head2 use P5EEx::Blue::P5EE
  
  Next, the P5EEx::Blue::P5EE module is included, which does the following:
  
    * disable "Use of uninitialized value" warnings
    * use Fatal;              # cause internal perl functions to throw exceptions
    * use Error;              # enable try/catch syntax
    * use Exception::Class;   # enable Exception inheritance
    * use P5EEx::Blue::Exceptions;  # define P5EE exceptions
  
  The base class of all P5EE exceptions is "P5EEx::Blue::Exception".
  Derived from this base exception, each component service of P5EE
  has its own base class as follows.
  
    P5EEx::Blue::Exception::Context
    P5EEx::Blue::Exception::Config
    P5EEx::Blue::Exception::Serializer
    P5EEx::Blue::Exception::Repository
    P5EEx::Blue::Exception::Security
    P5EEx::Blue::Exception::Session
    P5EEx::Blue::Exception::Widget
    P5EEx::Blue::Exception::TemplateEngine
    P5EEx::Blue::Exception::Procedure
    P5EEx::Blue::Exception::Messaging
    P5EEx::Blue::Exception::LogChannel
  
  =head2 cgi-bin/p5ee and bootstrapping the environment
  
  The "p5ee" program then executes the following line.
  
    my $context = P5EEx::Blue::P5EE->context(\%main::conf);
  
  This instantiates a $context object, passing the
  global %main::conf hash as an argument.
  
  Note that in other Context implementations other than
  CGI, the $context may survive to serve more than a single
  request or to dispatch many events coming from the network
  or from a user.  In that case, the
  P5EEx::Blue::P5EE->context() method may return an
  already-instantiated $context object.  The $context
  is a singleton per process.
  
  =head2 P5EEx::Blue::P5EE->context()
  
  If the "contextClass" variable is in the argument hash, it is used.
  Otherwise, if the "p5ee" program is running in the CGI context
  (HTTP_USER_AGENT environment variable is set) or at the
  command line, the P5EEx::Blue::Context::CGI class
  will be assumed.
  
  The code for the selected class is loaded and a $context
  of the appropriate class is instantiated.
  For the CGI context, the P5EEx::Blue::Context::CGI->new()
  method is called, and the arguments of the context() method
  call (%main::conf, in this case) are passed on to it.
  
  =head2 P5EEx::Blue::Context::CGI->new()
  
  The constructor (new()) for P5EEx::Blue::Context::CGI is actually provided
  by its parent class, P5EEx::Blue::Context.
  
  If the "configClass" was not specified in the arguments
  (%main::conf, in this case), P5EEx::Blue::Config::File is assumed.
  It is instantiated, once again passing on the hash of initialization args,
  and the result is stored in $self->{config};
  
  Then the P5EEx::Blue::Context::CGI->init() method is called to
  complete the $context constructor.  A CGI object is created
  and added to the %args to be passed on to Session instantiation.
  
  If the "sessionClass" was not specified in the arguments,
  P5EEx::Blue::Session::HTMLHidden is assumed.
  It is instantiated, once again passing on the hash of initialization args,
  and the result is stored in $self->{session};
  
  =head2 P5EEx::Blue::Config::File->new()
  
  The constructor (new()) for P5EEx::Blue::Config::File is actually provided
  by its grand-parent class, P5EEx::Blue::Reference.
  It creates a reference by calling P5EEx::Blue::Config::File->create(),
  blesses it into the class, calls init()
  (P5EEx::Blue::Reference->init()) which does nothing,
  and then returns the constructed Config::File object.
  
  The Config::File->create() method loads data from a configuration
  file and returns the reference to a hash.  But first it has to find
  the file and deserialize it.
  
  If the "configFile" was not specified in the arguments
  (%main::conf, in this case), the following files are searched for in
  order.  (If the script were renamed to "foo", it would look for "foo"
  variants of the files instead of "p5ee" variants of the files.)
  
     1. p5ee.pl           2. config.pl
     3. p5ee.xml          4. config.xml
     5. p5ee.ini          6. config.ini
     7. p5ee.properties   8. config.properties
     9. p5ee.perl        10. config.perl
    11. p5ee.conf        12. config.conf
  
  By convention, "config.pl" is the config file for P5EE CGI scripts.
  However, the first file that is found is assumed to be the relevant config file.
  If no config file is found or if it cannot be opened, an exception is thrown.
  Otherwise, the text is read in from the file and deserialized into
  a hash reference.
  
  If the "configSerializerClass" was not specified in the arguments
  (%main::conf, in this case), the file suffix for the config file
  is used to determine the Serializer class to use for deserialization.
  
    pl          # use "eval" instead of a serializer
    perl        # P5EEx::Blue::Serializer::Dumper (like "pl" but use a serializer)
    xml         # P5EEx::Blue::Serializer::XMLSimple
    ini         # P5EEx::Blue::Serializer::Ini
    properties  # P5EEx::Blue::Serializer::Properties
    conf        # P5EEx::Blue::Serializer::Properties
    stor        # P5EEx::Blue::Serializer::Storable
  
  If the file is a .pl file, no serializer is implied.  It is just eval'ed.
  (It must have "$var =" as the first non-whitespace text in the file,
  where "var" is any variable name.)
  
  If a Serializer is specified or implied, a serializer is instantiated
  and the config file data is deserialized into a hash reference.
  
  The resulting hash reference is returned and stored in $context->{config}.
  
  =head2 P5EEx::Blue::Context::CGI->init()
  
  The init() method is where the CGI object is created, parsing the
  environment variables and STDIN which are
  part of the CGI runtime environment.
  
  For debugging purposes, a "debugmode" variable is checked in the 
  %args (i.e. %main::conf) to see if
  special processing with the CGI object should be performed.
  If "debugmode" is "record", the CGI objects and the %ENV hash will
  be saved to files ("debug.vars" and "debug.env", respectively).
  If "debugmode" is "replay", the current %ENV and CGI will be
  cleared and loaded from the files from a previously recorded request.
  
  Another sort of debugging is initialized if the "debug"
  variable is supplied in the %args.  This turns on a global debug
  flag ($P5EEx::Blue::Context::DEBUG) and sets the debug scope
  (which classes or methods should produce debug output).
  
  To support migration of code, the CGI object can also be passed into
  the P5EE->context() method as an argument, and it will be used rather
  than trying to create a new one.  However, this is not the execution
  path being described here.
  
  =head2 P5EEx::Blue::Session::HTMLHidden->new()
  
  The constructor (new()) for P5EEx::Blue::Session::HTMLHidden is actually provided
  by its grand-parent class, P5EEx::Blue::Reference.
  It creates a hash reference by calling P5EEx::Blue::Reference->create(),
  blesses it into the class, calls init()
  (P5EEx::Blue::Session::HTMLHidden->init()),
  and then returns the constructed Session::HTMLHidden object.
  
  The init() method looks at the CGI variables in the request
  and restores the session state information from the variable
  named "p5ee.sessiondata" (and "p5ee.sessiondata[2..n]").
  When the values of these variables are concatenated, they
  form a Base64-encoded, gzipped, frozen multi-level hash of
  session state data.  To retrieve the state data, the text
  is therefore decoded, gunzipped, and thawed (a la Storable).
  This state is stored in $session->{state} and the session
  cache is initialized to an empty hashref.
  
  =head2 cgi-bin/p5ee and dispatching events
  
  The "p5ee" program finally executes the following line.
  
    $context->dispatch_events();
  
  This does everything necessary to dispatch events which are
  implied in the HTTP request, loading whatever data is needed,
  modifying it, and saving it again to await the next request.
  
  Please note that other Context implementations (i.e. Context::Modperl)
  use the same API, but they may already have database connections
  initialized, data already loaded, etc.  However, the basic
  CGI Context (Context::CGI) described here must initialize
  everything at the outset and shut it all down at the completion
  of each request.
  
  =head2 P5EEx::Blue::Context::CGI->dispatch_events()
  
  The CGI variables are examined.  Variables which start with "p5ee.event"
  are identified as events to be handled, and they are saved for later.
  These are called "event variables".
  
  All other variables are understood to be widget attributes and they
  are saved to their respective widgets. 
  
  Variables with any of the
  "{}[]" "indexing" characters (such as "table_editor{data}[1][5]") are
  called "indexed variables".  The value is saved to the "table_editor"
  widget, which evidently has an attribute called "data" which is a
  two dimensional array.
  
  Variables without the indexing characters but 
  with at least one dot (".") in them are "dotted variables"
  of the form "widgetname.attributename".  (This is a synonym for
  "widgetname{attributename}", but it is handled more efficiently.)
  Widget names may include dots, but attribute names may not.
  Thus, the last dot separates the widget name from the attribute name.
  So "app.toolbar.savebutton.width" is the "width" attribute on the
  "app.toolbar.savebutton" widget.
  
  Variables without indexing characters or dots are "plain variables".
  If the special variable "wname" was also supplied, all plain variables
  are understood to be attributes of the $wname widget.  Otherwise,
  all plain variables are stored in the "global" widget.
  
  After all variables are stored in the Session, events are handled.
  There are two kinds of events, "user events" (such as come from
  <input type=submit> and <input type=image> tags) and "callback events"
  (such as come from <input type=hidden> tags).
  
  User events have a name that looks like one of the following.
  
    p5ee.event.widgetname.eventname
    p5ee.event.widgetname.eventname(arg1)
    p5ee.event.widgetname.eventname(arg1,arg2,...)
  
  These events are parsed, the appropriate widget is summoned, and the
  widget is instructed to handle the event (i.e. $w->handle_event()).
  
  Callback events have a name that looks simply like "p5ee.event".
  The "widgetname.eventname(args)" is in the value of the variable.
  
  After all events have been handled, the context calls
  $self->display_current_widget().
  
  =head2 P5EEx::Blue::Context::CGI->display_current_widget()
  
  The display_current_widget() method is implemented in the parent
  class, P5EEx::Blue::Context::HTML.
  
  A attribute "global.current_widget" contains the name of the current
  widget to display. 
  
  If this is not set, check the CGI variable "wname"
  (and set global.current_widget if found).
  If this is not set, check the Initialization Config (i.e. the copy
  of %main::conf, stored in the $context) for a variable named
  "defaultWname".
  If this is not set, use the $PATH_INFO, with internal "/'s"
  converted to dots (i.e. "/app/selector" => "app.selector").
  Otherwise, use "default" as the current widget
  (and set global.current_widget).
  
  Then the current widget is summoned and handed to the display_items()
  method.
  
  =head2 P5EEx::Blue::Context::CGI->display_items()
  
  The job of display items is to take the list of args, convert them
  to HTML, and print them to STDOUT with the appropriate HTTP headers.
  Also, depending on the %main::conf and the browser capabilities,
  the content may be compressed (gzipped).
  
  That's it.  All of the processing for a single CGI request is complete.
  All application code is wrapped up in user interface widgets
  (P5EEx::Blue::Widget::HTML) and entity widgets (P5EEx::Blue::Widget::Entity).
  The user interface widgets are mostly prepackaged, but are configured
  through the configuration file, allowing for a data-driven programming
  style on the user interface.  The entity widgets store most of their state
  in Repositories.  They also get configured
  through the config file, but they are frequently subclassed to add
  additional functionality.
  
  =cut
  
  
  
  


Reply via email to