Application Development With Webware
++++++++++++++++++++++++++++++++++++

Webware for Python

:Version: 1.2.4
:Released: 03/19/22

.. contents:: Contents

Synopsis
========

Here we describe best practices for developing a web application using Webware.

Setting up your application
===========================

The first task in developing an application is to set up the file structure in
which you will be working.

It is possible to put your application in a subdirectory under ``WebKit/`` and
change ``WebKit/Configs/Application.config`` to add another context.
But *do not do this*.  Your application will be entwined with the Webware
installation, making it difficult to upgrade Webware, and difficult to identify
your own files from Webware files.

Creating a Working Directory
----------------------------

Instead you should use the script ``bin/MakeAppWorkDir.py``.
You should run it like::

    $ python Webware/bin/MakeAppWorkDir -c Context -l Lib --gitignore WorkDir

This will create a directory ``WorkDir`` that will contain a directory structure
for your application.  The options are:

``-c Context``:
    Use ``Context`` as the name for the application default context.
    A subdirectory with the same name will be created in the work dir (you can
    change that with the ``-d`` option).
    If you do not use the ``-c`` option, the context name will be ``MyContext``.
    I like the name ``Context`` for all my applications.
``-l Lib``:
    Create a ``Lib`` directory in the work dir which will be added to the Python
    path.  You can use the ``-l`` option multiple times; and you can also add
    already existent library directories outside of the work dir.
``--gitignore``:
    Create a ``.gitignore`` file for use with Git.
``WorkDir``:
    The files will be put here.  Name if after your application, place it where
    it is convenient for you -- it doesn't need to be located close to the
    Webware installation.

You can see all available options if you run ``Webware/bin/MakeAppWorkDir.py``
without any parameters.

When you do this, you'll see this directory structure::

    AppServer*  Configs/  error404.html  Launch.py  Logs/      WebKit.cgi
    Cache/      Context/  ErrorMsgs/     Lib/       Sessions/  webkit*

Here's what the files and directories are for:

``AppServer``:
    The script to start up the AppServer for this application.
    Each application will have its own AppServer, and its own process.
    If you are running under Windows, you will see a ``AppServer.bat``
    instead and additionally, you will find a ``AppServerService.py``
    script that can be used to start the AppServer as a service.
``Cache``:
    A directory containing cache files.  You won't need to look in here.
``Configs``:
    Configuration files for the application.  These files are copied from
    ``WebKit/Configs``, but are specific to this application/AppServer.
``Context``:
    The directory for your default context.  This is where you put your servlets.
    You can change its name and location with the ```-c`` and ``-d`` options.
    You can also change this subsequently in the ``Application.config`` file
    in the ``Configs`` directory, where you can also configure more than one
    context.  You may also want to remove the other standard contexts that come
    with Webware from the config file.
``error404.html``:
    The static HTML page to be displayed when a page is not found.  You can
    remove this to display a standard error message, modify the page according
    to your preferences, or use a custom error servlet instead by setting
    ``ErrorPage`` in the ``Application.config`` file appropriately.
``ErrorMsgs``:
    HTML pages for any errors that occur.  These can pile up and take up
    considerable size (even just during development), so you'll want to
    purge these every so often.
``Launch.py``:
    Called by the ``AppServer`` script to launch the AppServer.
``Lib``:
    An example for an application-specific library package that can be created
    ``-l`` option (in this case, ``-l Lib``).  Import modules from this directory
    like ``from Lib.SitePage import SitePage``.
``Logs``:
    Logs of accesses.
``Sessions``:
    Users sessions.  These should be cleaned out automatically, you won't
    have to look in this directory.
``WebKit.cgi``:
    A CGI script/adapter for accessing the AppServer here.  You can still use
    the other adapters, but most of them don't need to be configured for the
    individual applications.  I still recommend ``mod_webkit`` or ``wkcgi``.
``webkit*``:
    If you are running under Unix, you can use this as a start script
    (see the `WebKit Install Guide`__).

__ InstallGuide.html


Using a version control system for your application
---------------------------------------------------

A version control system is a useful tool for managing your application.
Popular Open Source version control systems are Subversion (SVN),
Mercurial (hg) and, increasingly, Git.  These systems handle versioning,
but they also make it possible for other people to see snapshots of your
progress, for multiple developers to collaborate and work on an application
simultaneously, and they create a sort of implicit file share for your project.
Even if you are the only developer on an application, a version control system
can be very helpful.

The working directory is a good place to start for creating a versioned project.
Assuming you're using Git, you can get started by creating a repository and
importing your project into the repository simply by running::

    $ cd WorkDir
    $ git init
    $ git add .
    $ git commit -m 'initial import'

You should use the option ``--gitignore`` when running ``MakeAppWorkDir.py``
if you plan to do this.  If you do, then ``.gitignore`` files will be added
to the working directory.  This tells Git to ignore files with certain
extensions (such as ``.pyc`` files), and all the files in certain directories
(``Cache``, ``ErrorMsgs``, ``Logs``, and ``Sessions``).
You shouldn't otherwise notice these files, even if you aren't using Git.

The command to import your project into a SVN repository is very similar::

    $ cd WorkDir
    $ svn import  -m 'initial import' https://myserver/myrepos/MyWebWareProject

Replace ``https://myserver/myrepos/`` with the URL of your SVN repository.
The ``.gitignore`` file will not be used in this case. Instead, you have to set
the ``svn:ignore`` property on the respective directory.  You can still use the
``.gitignore`` files to generate the necessary ``svn propset`` commands::

    $ find . -name .gitignore | while read f; \
    >   do echo svn propset svn:ignore -F $f $(dirname $f); done

After importing ``WorkDir`` to the SVN repository, note that it is not
automatically under version control.  To start working, you first need to
explicitly check it out from the repository using ``svn checkout``.


Using the working directory from multiple accounts
--------------------------------------------------

If you are using a version control system or if you are otherwise distributing
your application code, you may find that it is difficult to manage the
differences between accounts.  For instance, in different accounts on different
machines Webware may be installed in different locations.  You may have the
actual directory in a different location as well -- it may be in
``~/webware/WorkDir`` for your active development, but ``/var/webware/WorkDir``
for the production version.  And if there are multiple development copies on the
same machine, you have to be sure they each use different adapter ports.

To solve these problems I recommend creating a shell script to handle startup.
I generally call this script ``start``, and it looks something like this::

    #!/bin/sh

    # lothlorien.colorstudy.com is my development machine
    if [ `hostname` = lothlorien.colorstudy.com ] ; then
        WORKING=$HOME/prog/webware/WorkingDir
        WEBWARE=$HOME/prog/webware/Webware
        OPS="AppServer.AutoReload=1"
    fi

    # this is my production environment
    if [ `hostname` = color.colorstudy.com && `whoami` = webware ] ; then
        WORKING=/www/WorkingDir
        WEBWARE=/www/Webware
        OPS=""
    fi

    if [ "$WORKING" = "" ] ; then
        echo I do not recognize this environment
        exit 1
    fi

    cd $WORKING
    ./AppServer --work-dir=$WORKING --webware-dir=$WEBWARE $OPS $*

You can add this to your project in the repository, and the script should
automatically detect what environment it is being used in.  You can use options
to change configuration parameters, like setting some parameters depending on
whether the environment is a development or production environment.

Some options that you may be particularly interested in:

``AppServer.AutoReload``:
    Setting this to ``1`` will make the AppServer restart if there have been
    changes to any loaded files.  This is very nice during development.
``AppServer.AdapterPort``:
    If you want multiple applications running on the same machine (e.g., one
    for development, one for production), you have to use different ports.
``Application.ShowDebugInfoOnErrors``:
    You probably don't want to have this on in production, but it's nice
    during development.
``Application.SaveErrorMessages``:
    During development you probably want this off.
``Application.EmailErrors``:
    Turn on for production.

For more settings, see the Configuration__ document.

__ Configuration.html

Structuring your Code
=====================

Once you've got the basic files and directories in place, you're ready to go in
and write some code.  Don't let this document get in the way of developing the
application how you choose, but here are some common patterns that have proven
useful for Webware applications.

SitePage
--------

Subclass a ``SitePage`` from ``WebKit.Page`` for your application.  This subclass
will change some methods and add some new methods.  It serves as the basis and
as a template for all the pages that follow.

Some code you may wish to include in your ``SitePage``:

* Authentication and security
* Accessing common objects (e.g., a user object, or a document object)
* Page header and footer
* Common layout commands, like ``writeHeader``
* Database access

I also typically add other functions to the SitePage module, and then do
``from Lib.SitePage import *`` in each servlet -- this might include functions
like htmlEncode, or some other select functions that I use constantly in
web applications.  Whether you want to use functions or methods is up to you --
in many cases methods can be more easily extended or customized later, but
sometimes method use can become excessive and create unnecessary dependencies
in your code.

A basic framework for your SitePage might be::

    from WebKit.Page import Page

    class SitePage(Page):

        def respond(self, trans):
            if self.securePage():
                if not self.session().value('username', False):
                    self.respondLogIn()
                    return

        def securePage(self):
            """Override this method in your servlets to return True if the
            page should only be accessible to logged-in users -- by default
            pages are publicly viewable"""
            return False

        def respondLogin(self):
            # Here we should deal with logging in...
            pass

Obviously there are a lot of details to add in on your own which are specific
to your application and the security and user model you are using.
