MiddleKit and WebKit

MiddleKit and WebKit

Getting Set up

Unfortunately, the MiddleKit docs don't do a good job of describing the use of MiddleKit from WebKit. This page will be a place to grow such documentation.

The most commonly asked question is about getting a store going for the WebKit application. Here is a reply I made to that question:

In my own projects, I put the store in a module so it
becomes a singleton. Something along these lines:

Store.py
-----------------------------------------------
import os
from MiddleKit.Run.MySQLObjectStore import MySQLObjectStore

# set user and password, maybe from config file

# Create the store
store = MySQLObjectStore(user, password)

# Read the model
dn = os.path.dirname
_filename = os.path.join(dn(dn(dn(__file__))), 'Core/UsedCars.mkmodel')
assert os.path.exists(_filename)
store.readModelFileNamed(_filename)
-----------------------------------------------

Then in SitePage.py:

from Store import store

class SitePage(Page):
    def __init__(self):
        self.store = store

So that my page subclasses can conveniently say "self.store"
when they need it.

Since module level code is executed only once, regardless of
the number of imports, you get one store for the whole
application.

You can use this same "module" technique for any other
application-wide singletons you like.


BTW You now have to consider the implications of using these
objects from multiple threads. Something you'd have to think
about even if you weren't using MK.

One alternative to the above approach is to create one store
per user. That works well if your users don't share objects
and you take care to remove the reference to the store when
a user's session expires.

I'm using the first approach, but plan on moving to the
second at some point in the future.

Another approach might be to put locks around certain
operations.

-- ChuckEsterbrook - 29 Mar 2002

Saving Middle Objects in the session

It makes sense to want to store Middle objects in the session, so that these objects can be retrieved easily. There are a few issues to consider with this:

My solution to this problem is to implement an alternate encoder/decoder for WebKit.SessionStore which recognizes Middle objects, and simply pickles a reference to the object. When reading in sessions from disk, the decoder finds the references and fetches the full Middle objects from the store.

This is transparent to user code -- you can simply put the Middle objects in the session, and it will work.

Note that this implementation assumes you have a single global store for your application.

Also, if you're using Webware 0.7 or older, you'll need to apply <a href="http://www.opensky.ca/~jdhildeb/webware.session.patch">this small patch</a> so that calling setEncoderDecoder will work properly when using the Dynamic session store.

MiddlePickle.py:

from pickle import Pickler as _Pickler
from pickle import Unpickler as _Unpickler
from MiddleKit.Run.MiddleObject import MiddleObject
_store = None

class Pickler(_Pickler):
    def persistent_id( self, object ):
        if isinstance( object, MiddleObject ) and object.isInStore():
            return object.sqlObjRef()
        else:
            return None

class Unpickler( _Unpickler ):
    def __init__(self,file,store=None):
        _Unpickler.__init__(self,file)
        if not store:
            store = _store
        assert store
        self._store = store

    def persistent_load( self, pid ):
        return self._store.fetchObjRef(long(pid))

    def setStore(store):
        global _store
        _store = store

    def dump(object, file, bin = 0):
        Pickler(file, bin).dump(object)

    def load(file,store=None):
        return Unpickler(file,store).load()

Setting it up

You'll have to tell the session handler to use this new implementation for encoding/decoding objects. Use the contextInitialize() function for this, which Webware will invoke to initialize your application. For example, put the following in your MyContext/__init__.py file:

def contextInitialize(application, path):
    # I have a "Global" module in which I store
    # a ref to my global store in this module
    import Global

    # initialize your store
    from MiddleKit.Run.MySQLObjectStore import MySQLObjectStore
    Global.store = MySQLObjectStore(user=os.environ['DBUSER'])
    modeldir = os.path.join( os.environ['APPDIR'], 'Lib/Middle' , os.environ['MIDDLEMODEL'])
    Global.store.readModelFileNamed(modeldir)

    # now that you have your global store, set the new encoder/decoder
    import MiddlePickle
    MiddlePickle.setStore(store)
    application.sessions().setEncoderDecoder(MiddlePickle.dump,
                                             MiddlePickle.load)

-- JasonHildebrand - 18 Feb 2003