NAME

OpenInteract2::Manual::Architecture - Overview of the OpenInteract2 Architecture

SYNOPSIS

This part of the OpenInteract2 manual describes the major pieces of the system and traces a users's request from catching the URL to returning the content.

CONTEXT

Overview

The Context (abbrev: CTX) glues the system together and is therefore used everywhere. It holds all application configuration information and provides a central lookup mechanism for actions, content generators, controllers and SPOPS object classes.

It is a singleton (there's only one in the system at any time) and can be imported from the OpenInteract2::Context class since it's used fairly often.

Creating the Context

Creating the context is one of the first actions you take when starting an OI2 server. While it can be created without referencing a website it's not much use. (You should only need do this when bootstrapping a new website into existence, and this is already done for you in OpenInteract2::Manage::Website::Create.

So normally it looks something like this:

my $ctx = OpenInteract2::Context->create(
                    { website_dir => $website_dir });

Once it's created the CTX symbol can be imported and used anywhere.

ADAPTER

Overview

The job of the adapter is to translate the world's information to something understandable by OpenInteract and then translate what OpenInteract generates into information for the outside world. So it sits between your interface (e.g., Apache/mod_perl, CGI, etc.) and the OpenInteract server. The information it translates from the outside world includes parameters from the user, user authentication, information about the request (hostname, URL, referer, cookies, etc.) and other data. It places these data into the relevant OpenInteract2::Request subclass.

Once the OpenInteract cycle is complete the adapter translates OpenInteract data (content, headers, etc.) into a response to send back to the user via the relevant OpenInteract2::Response subclass. For an example see Apache::OpenInteract2.

Creating your own adapter

Creating an adapter is not difficult. Adapter classes tend to be fairly short as most of the work is done in in the OpenInteract2::Request/OpenInteract2::Response subclasses. For instance, here's the full adapter for Apache/mod_perl 1.x:

 1: package Apache::OpenInteract2;
 2: 
 3: use strict;
 4: use Log::Log4perl            qw( get_logger );
 5: use OpenInteract2::Auth;
 6: use OpenInteract2::Constants qw( :log );
 7: use OpenInteract2::Context   qw( CTX );
 8: use OpenInteract2::Request;
 9: use OpenInteract2::Response;
10: 
11: sub handler($$) {
12:     my ( $class, $r ) = @_;
13:     my $log = get_logger( LOG_OI );
14: 
15:     $log->is_info &&
16:         $log->info( scalar( localtime ), ": request from ",
17:                     "[", $r->connection->remote_ip, "] for URL ",
18:                     "[", $r->uri, '?', scalar( $r->args ), "]" );
19: 
20:     my $response = OpenInteract2::Response->new({ apache => $r });
21:     my $request  = OpenInteract2::Request->new({ apache => $r });
22: 
23:     OpenInteract2::Auth->login( $r->pnotes( 'login_user' ) );
24: 
25:     my $controller = eval {
26:         OpenInteract2::Controller->new( $request, $response )
27:     };
28:     if ( $@ ) {
29:         $response->content( $@ );
30:     }
31:     else {
32:         $controller->execute;
33:     }
34:     $response->send;
35:     return $response->status;
36: }
37: 
38: 1;

Very easy -- it's only about 15 lines if you remove the logging! This even has a little twist by passing in the 'login_user' key from the Apache pnotes (line 23), which is a hook to the Apache::OpenInteract2::HttpAuth class to allow HTTP (rather than cookie) authentication.

Some gotchas to note:

  • Create response before request - It may seem backwards but you need to create the response object before the request object. (This is due to a dependency.)

Additionally if your adapter is more of a standalone service (like the oi2_daemon) that spawns off children/threads for requests, you need to also be aware of the following:

  • Initialize logging - You'll need to initialize log4perl. This is normally as simple as passing a parameter to the create method of OpenInteract2::Context, but you can also use one of the methods in OpenInteract2::Log.

  • Close all database connections - Before spawning off children/threads from the parent you MUST shutdown all database connections. They won't survive the fork/thread and you'll get very strange errors. Do this with the shutdown method in OpenInteract2::DatasourceManager.

CONTROLLER

Overview

Once the adapter has created the request and response it hands off the processing to the OpenInteract2::Controller object. Now we're entirely inside the OI2 server environment. This reads the action and task from the request and creates the relevant action object that will generate the content. It knows which action object to create through a URL-to-action mapping created at Context startup. The most-used controller (OpenInteract2::Controller::MainTemplate) places the generated content in a larger scope so you can control common graphical elements (sidebars, menus, etc.) from once place. Another controller (OpenInteract2::Controller::Raw) returns the content as-is.

ACTION

Overview

Actions are the core of OpenInteract2. Each action provides a discrete set of functionality. What "discrete set" means is up to the developer, but typically this is a set of SCRUD (Search - CReate - Update - Delete) operations on a class of objects.

Each action is represented by zero or more URLs, and each operation is specified by a task referenced in that URL. So if I created a 'news' action my URLs might look like:

Every task returns some sort of content, generally by passing data to a Content Generator which marries it with a template. See OpenInteract2::Action for much more information.

CONTENT GENERATOR

Overview

As mentioned above tasks in an Action return content. They normally generate that content by assembling a set of data and passing that data off to a content generator. A content generator is a wrapper around some sort of templating system, such as the Template Toolkit, HTML::Template or Text::Template or even your own homegrown system. (Admit it, you've written your own.)

Each action is associated with a content generator. And you can even associate an action with multiple content generators so you can settle a bet as to which templating system is easiest to use.

TRACING A REQUEST

Now we'll trace a request throughout OpenInteract.

Step 0: Startup Tasks

The adapter or another process (e.g., 'startup.pl' in a mod_perl setup) will run a number of processes at server startup. This includes:

  • Initialize logging

  • Create context with website directory

  • Assign the proper request/response types to request

  • If threaded/forking: disconnect all database handles.

Step 1: Request Meets Adapter

This step is a little fuzzy by necessity: we purposefully don't know in what form the request is coming in or how the adapter handles it.

Step 2: Adapter Creates Request/Response Objects

The adapter creates the OpenInteract2::Response and OpenInteract2::Request objects, in that order. Each one has necessary initialization steps. In particular the request object will read the necessary headers, parameters, uploaded files, cookies and create the session from the cookie.

It also finds the 'relevant' part of the URL and determines the action and task from it. The 'relevant' part is what's leftover after the URL-space (aka, deployment context, set in the context_info.deployed_under server configuration key) is lopped off.

Step 3: Adapter Logs in User

It can optionally handle extra authentication as this point such as HTTP auth or some other capability. Generally this will consist of retrieving a user object created from some other part of the system or creating a user object based on trusted information (like a user ID) from another area.

If available this user object is passed to the login method of the OpenInteract2::Auth class so it has a head start.

Step 4: Adapter Creates Controller

Adapter creates the OpenInteract2::Controller object with the request and response objects created earlier.

The controller asks the request for the action name and asks the context for an action object of that name. (It's always a subclass of OpenInteract2::Action.) If the action name exists but is not found we use the action named in the action_info.not_found server configuration key. If the action name doesn't exist we use the action from action_info.none.

Once the action is found we assign it the task (if available) as reported by the request.

Step 5: Adapter Executes Controller

If created properly the adapter calls execute() on the controller. This starts the content generation process running.

The controller will call execute() on the action which returns the content for it.

Step 6: Action Finds Task

he action needs to find which task to execute. Normally this is as simple as getting the value of the task property. But the individual action can override this, or if no task was specified we use the value of task_default.

Step 7: Action Checks Validity

Find out if the task is invalid. This means:

  • It doesn't start with a '_'

  • It's not listed in task_invalid

  • If task_valid defined, it is listed there.

If the task is valid, then we ensure that this user has the proper security level to execute it.

Step 8: Action Generates Content

First, we check the cache to see if content exists and if it does, we return it without going any further.

Next we execute the method specified by what we've determined to be the task. (This is almost certainly the method with the same name as the task.)

An action can generate content by itself but most times it just gathers the necessary data and passes it, along with a template specification, to a content generator which returns the content for the action.

If the cache is activated for this method we'll cache the content. In any case we return the content, finishing the flow for the action and moving back up to the controller.

Step 9: Controller Places Action Content in Larger Scope (optional)

The main action is done and has returned its content to the controller. One controller (OpenInteract2::Controller::Raw) will just return this content and call it a day.

Most times you'll want to take that content and put it into another template. The controller just instantiates a new content generator and goes through the same process as the action, passing it a set of data (of which the generated action content is part) and a template specification (normally from the 'main_template' theme property).

Oftentimes the main template will hold multiple discrete actions of its own. For example, the default main template shipped with OI has an action to generate the list of boxes that appears on the right-hand side. You could trigger an action to get the latest weather conditions, webcam photo, news headlines, whatever you wish.

Each of these actions is just like any other and goes through the same process listed above.

Step 10: Controller Sets Content to Response

Whether it's the action content or the scoped content (for lack of a better name), we set the content in the response object, which hasn't done much until now except hold the occasional outgoing cookie.

The controller's job is done and flow now returns back up a level to the adapter.

Step 11: Adapter Asks Response to Send

The only job left of the adapter is to ask the response to send the content.

Step 12: Adapter Cleans Up

The adapter can do any necessary cleanup.

COPYRIGHT

Copyright (c) 2002 Chris Winters. All rights reserved.

AUTHORS

Chris Winters <chris@cwinters.com>