Application structure
Components
- Browser/Javascript based web-application
- Request handling
- Perl application logic (mostly left over from pre-1.3)
- PostgreSQL based authentication and authorization framework
- PL/pgSQL based stored procedures (application logic)
- PostgreSQL based data storage (tables) with SQL and PL/pgSQL based data integrity checks
Browser/web application
Before 1.5, all state changes in the browser were handled through the traditional CGI paradigm of "Submit+reload page", with some minor Javascript (JS) helpers to hide/show screen sections.
As of 1.5, there are other options due to more tight integration with the Dojo framework. In 1.5 pages are still reloaded using the "Submit+reload page" paradigm (mostly), but instead of letting the browser do so in a frame, the reloads are handled as asynchronous XHR requests.
Request handling
Webserver/Perl web-glue
Multiple web-frontends are supported, through the web-glue framework Plack. For development purposes, Starman is the easiest way to get started.
Dispatching
The general dispatch cycle works like this:
- The PSGI compatible server receives a request
- Custom-LedgerSMB Plack middleware performs request-wrapping functions, including
- Verification of credentials
- Loading the required workflow script
- Plack parses the request and dispatches to the configured route handlers
- Dispatch for pre-1.3 code
- Dispatch for post-1.3 code
Note that dispatching differs between pre- and post-1.3 code, but that the dispatch mechanism described below was implemented in the 1.6 development cycle.
Dispatching to post-1.3 code
After the Middleware wrappers have done their job, this sequence of events occurs:
- The function psgi_app in LedgerSMB::PSGI receives the request environment
- A LedgerSMB::Auth object is instantiated
- A LedgerSMB object is instantiated, from the request data and the LedgerSMB::Auth object
- A request-local environment is set up binding global variables to the values from the request (e.g. $LedgerSMB::App_State::User)
- The request is dispatched to the script's entry point, with the LedgerSMB instance as a parameter
- The entry point returns the PSGI result triplet to the dispatcher (psgi_app)
- The dispatch routine cleans up after the request, committing the transaction on success or rolling back the transaction in case of error and "unbinding" the global variables
- The dispatch routine returns the triplet to the Plack caller
Dispatching to pre-1.3 code
- The PSGI "app" returned by "old_app" in LedgerSMB::PSGI sets up a full CGI environment based on the PSGI environment
- After creating a full CGI environment, the STDOUT handle is bound to a temporary file
- The executing process is forked (in the _run_old() function of LedgerSMB::PSGI)
- The forked process transfers control to 'old-handler.pl' for request dispatch
- old-handler sets up a $form object (LedgerSMB/Form.pm) and creates a database transaction
- old-handler loads a 'workflow script' from old/bin/
- old-handler invokes the function specified in the 'action' query parameter
Note: this could also be an 'action' or 'nextsub' POST parameter - the invoked action generates the response by printing bits and pieces of the required HTML response inline with the code to decode web input and handling state change
- old-handler.pl cleans up after the request, committing the transaction on success or rolling back in case of an error
- the forking process continues processing by reading the CGI output generated by the forked process
- the request handler passes the PSGI response triplet to the Plack caller based on the parsed CGI output
Perl application logic
- The entry point constructs ORM mapper objects where necessary
- The entry point invokes the required state changes where applicable
- The entry point determines which output screens to generate, selecting a template from the UI/ directory
- The entry point passes the collected results and template to the rendering engine (LedgerSMB::Template::UI)
- The rendering engine generates the output, request headers and result code
Retrieving database information
Changing database information state