MiddleKit and WebKit
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
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:
since Middle objects contain database connections, they cannot be pickled (stored to disk) in a straightforward fashion
you wouldn't want the data fields pickled anyways, due to concurrency issues. For example, if an object is pickled on disk in one user's session, and then gets modified (in-memory) by another user, you now have two copies of the object with different field values. How do you resolve 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()
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