Cache.py :  » Web-Frameworks » Zope » Zope-2.6.0 » lib » python » OFS » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Zope 
Zope » Zope 2.6.0 » lib » python » OFS » Cache.py
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__="""Cacheable object and cache management base classes.

$Id: Cache.py,v 1.10 2002/08/14 21:42:56 mj Exp $"""

__version__='$Revision: 1.10 $'[11:-2]

import time, sys
import Globals
from Globals import DTMLFile
from Acquisition import aq_get,aq_acquire,aq_inner,aq_parent,aq_base
from zLOG import LOG,WARNING
from AccessControl import getSecurityManager
from AccessControl.Role import _isBeingUsedAsAMethod
from AccessControl import Unauthorized

ZCM_MANAGERS = '__ZCacheManager_ids__'

ViewManagementScreensPermission = 'View management screens'
ChangeCacheSettingsPermission = 'Change cache settings'


def isCacheable(ob):
    return getattr(aq_base(ob), '_isCacheable', 0)

def managersExist(ob):
    # Returns 1 if any CacheManagers exist in the context of ob.
    if aq_get(ob, ZCM_MANAGERS, None, 1):
        return 1
    return 0

def filterCacheTab(ob):
    if _isBeingUsedAsAMethod(ob):
        # Show tab when in a ZClass def that uses Cacheable as a base.
        parent = aq_parent(aq_inner(ob))
        return isCacheable(parent)
    else:
        return managersExist(ob)

def filterCacheManagers(orig, container, name, value, extra):
    '''
    This is a filter method for aq_acquire.
    It causes objects to be found only if they are
    in the list of cache managers.
    '''
    if (hasattr(aq_base(container), ZCM_MANAGERS) and
        name in getattr(container, ZCM_MANAGERS)):
        return 1
    return 0

def getVerifiedManagerIds(container):
    '''
    Gets the list of cache managers in a container, verifying each one.
    '''
    ids = getattr(container, ZCM_MANAGERS, ())
    rval = []
    for id in ids:
        if getattr(getattr(container, id, None), '_isCacheManager', 0):
            rval.append(id)
    return tuple(rval)


# Anytime a CacheManager is added or removed, all _v_ZCacheable_cache
# attributes must be invalidated.  manager_timestamp is a way to do
# that.
manager_timestamp = 0


class Cacheable:
    '''Mix-in for cacheable objects.
    '''

    manage_options = ({
        'label':'Cache',
        'action':'ZCacheable_manage',
        'filter':filterCacheTab,
        'help':('OFSP','Cacheable-properties.stx'),
        },)

    __ac_permissions__ = (
        (ViewManagementScreensPermission,
         ('ZCacheable_manage',
          'ZCacheable_invalidate',
          'ZCacheable_isMethod',
          'ZCacheable_enabled',
          'ZCacheable_getManagerId',
          'ZCacheable_getManagerIds',
          'ZCacheable_configHTML',
          )),
        (ChangeCacheSettingsPermission,
         ('ZCacheable_setManagerId',
          'ZCacheable_setEnabled',
          ), ('Manager',)),
        )

    ZCacheable_manage = DTMLFile('dtml/cacheable', globals())

    _v_ZCacheable_cache = None
    _v_ZCacheable_manager_timestamp = 0
    __manager_id = None
    __enabled = 1
    _isCacheable = 1

    ZCacheable_getManager__roles__ = ()
    def ZCacheable_getManager(self):
        '''Returns the currently associated cache manager.'''
        manager_id = self.__manager_id
        if manager_id is None:
            return None
        try:
            return aq_acquire(
                self, manager_id, containment=1,
                filter=filterCacheManagers, extra=None, default=None)
        except AttributeError:
            return None

    ZCacheable_getCache__roles__ = ()
    def ZCacheable_getCache(self):
        '''Gets the cache associated with this object.
        '''
        if self.__manager_id is None:
            return None
        c = self._v_ZCacheable_cache
        if c is not None:
            # We have a volatile reference to the cache.
            if self._v_ZCacheable_manager_timestamp == manager_timestamp:
                return aq_base(c)
        manager = self.ZCacheable_getManager()
        if manager is not None:
            c = aq_base(manager.ZCacheManager_getCache())
        else:
            return None
        # Set a volatile reference to the cache then return it.
        self._v_ZCacheable_cache = c
        self._v_ZCacheable_manager_timestamp = manager_timestamp
        return c

    ZCacheable_isCachingEnabled__roles__ = ()
    def ZCacheable_isCachingEnabled(self):
        '''
        Returns true only if associated with a cache manager and
        caching of this method is enabled.
        '''
        return self.__enabled and self.ZCacheable_getCache()

    def ZCacheable_isAMethod(self):
        '''
        Returns 1 when this object is a ZClass method.
        '''
        m = _isBeingUsedAsAMethod(self)
        return m

    ZCacheable_getObAndView__roles__ = ()
    def ZCacheable_getObAndView(self, view_name):
        """
        If this object is a method of a ZClass and we're working
        with the primary view, uses the ZClass instance as ob
        and our own ID as the view_name.  Otherwise returns
        self and view_name unchanged.
        """
        ob = self
        if not view_name and self.ZCacheable_isAMethod():
            # This is a ZClass method.
            ob = aq_parent(aq_inner(self))
            if isCacheable(ob):
                view_name = self.getId()
            else:
                # Both the parent and the child have to be
                # cacheable.
                ob = self
        return ob, view_name

    ZCacheable_get__roles__ = ()
    def ZCacheable_get(self, view_name='', keywords=None,
                       mtime_func=None, default=None):
        '''Retrieves the cached view for the object under the
        conditions specified by keywords. If the value is
        not yet cached, returns the default.
        '''
        c = self.ZCacheable_getCache()
        if c is not None and self.__enabled:
            ob, view_name = self.ZCacheable_getObAndView(view_name)
            try:
                val = c.ZCache_get(ob, view_name, keywords,
                                   mtime_func, default)
                return val
            except:
                LOG('Cache', WARNING, 'ZCache_get() exception',
                    error=sys.exc_info())
                return default
        return default

    ZCacheable_set__roles__ = ()
    def ZCacheable_set(self, data, view_name='', keywords=None,
                       mtime_func=None):
        '''Cacheable views should call this method after generating
        cacheable results. The data argument can be of any Python type.
        '''
        c = self.ZCacheable_getCache()
        if c is not None and self.__enabled:
            ob, view_name = self.ZCacheable_getObAndView(view_name)
            try:
                c.ZCache_set(ob, data, view_name, keywords,
                             mtime_func)
            except:
                LOG('Cache', WARNING, 'ZCache_set() exception',
                    error=sys.exc_info())

    def ZCacheable_invalidate(self, view_name='', REQUEST=None):
        '''Called after a cacheable object is edited. Causes all
        cache entries that apply to the view_name to be removed.
        Returns a status message.
        '''
        c = self.ZCacheable_getCache()
        if c is not None:
            ob, view_name = self.ZCacheable_getObAndView(view_name)
            try:
                message = c.ZCache_invalidate(ob)
                if not message:
                    message = 'Invalidated.'
            except:
                exc = sys.exc_info()
                try:
                    LOG('Cache', WARNING, 'ZCache_invalidate() exception',
                        error=exc)
                    message = 'An exception occurred: %s: %s' % exc[:2]
                finally:
                    exc = None
        else:
            message = 'This object is not associated with a cache manager.'
        if REQUEST is not None:
            return self.ZCacheable_manage(
                self, REQUEST, management_view='Cache',
                manage_tabs_message=message)
        else:
            return message

    ZCacheable_getModTime__roles__=()
    def ZCacheable_getModTime(self, mtime_func=None):
        '''Returns the highest of the last mod times.'''
        # Based on:
        #   mtime_func
        #   self.mtime
        #   self.__class__.mtime
        #   (if in a ZClass) zclass_instance.mtime
        #                    zclass_instance.__class__.mtime
        mtime = 0
        if mtime_func:
            # Allow mtime_func to influence the mod time.
            mtime = mtime_func()
        base = aq_base(self)
        mtime = max(getattr(base, '_p_mtime', mtime), mtime)
        klass = getattr(base, '__class__', None)
        if klass:
            mtime = max(getattr(klass, '_p_mtime', mtime), mtime)
        if self.ZCacheable_isAMethod():
            # This is a ZClass method.
            instance = aq_parent(aq_inner(self))
            base = aq_base(instance)
            mtime = max(getattr(base, '_p_mtime', mtime), mtime)
            klass = getattr(base, '__class__', None)
            if klass:
                mtime = max(getattr(klass, '_p_mtime', mtime), mtime)
        return mtime

    def ZCacheable_getManagerId(self):
        '''Returns the id of the current ZCacheManager.'''
        return self.__manager_id

    def ZCacheable_getManagerURL(self):
        '''Returns the URL of the current ZCacheManager.'''
        manager = self.ZCacheable_getManager()
        if manager is not None:
            return manager.absolute_url()
        return None

    def ZCacheable_getManagerIds(self):
        '''Returns a list of mappings containing the id and title
        of the available ZCacheManagers.'''
        rval = []
        ob = self
        used_ids = {}
        while ob is not None:
            if hasattr(aq_base(ob), ZCM_MANAGERS):
                ids = getattr(ob, ZCM_MANAGERS)
                for id in ids:
                    manager = getattr(ob, id, None)
                    if manager is not None:
                        id = manager.getId()
                        if not used_ids.has_key(id):
                            title = getattr(aq_base(manager),
                                            'title', '')
                            rval.append({'id':id, 'title':title})
                            used_ids[id] = 1
            ob = aq_parent(aq_inner(ob))
        return tuple(rval)

    def ZCacheable_setManagerId(self, manager_id, REQUEST=None):
        '''Changes the manager_id for this object.'''
        self.ZCacheable_invalidate()
        if not manager_id:
            # User requested disassociation
            # from the cache manager.
            manager_id = None
        else:
            manager_id = str(manager_id)
        self.__manager_id = manager_id
        self._v_ZCacheable_cache = None
        if REQUEST is not None:
            return self.ZCacheable_manage(
                self, REQUEST, management_view='Cache',
                manage_tabs_message='Cache settings changed.')

    def ZCacheable_enabled(self):
        '''Returns true if caching is enabled for this object
        or method.'''
        return self.__enabled

    def ZCacheable_setEnabled(self, enabled=0, REQUEST=None):
        '''Changes the enabled flag. Normally used only when
        setting up cacheable ZClass methods.'''
        self.__enabled = enabled and 1 or 0
        if REQUEST is not None:
            return self.ZCacheable_manage(
                self, REQUEST, management_view='Cache',
                manage_tabs_message='Cache settings changed.')

    def ZCacheable_configHTML(self):
        '''Override to provide configuration of caching
        behavior that can only be specific to the cacheable object.
        '''
        return ''


Globals.default__class_init__(Cacheable)


def findCacheables(ob, manager_id, require_assoc, subfolders,
                   meta_types, rval, path):
    '''
    Used by the CacheManager UI.  Recursive.  Similar to the Zope
    "Find" function.  Finds all Cacheable objects in a hierarchy.
    '''
    try:
        if meta_types:
            subobs = ob.objectValues(meta_types)
        else:
            subobs = ob.objectValues()
        sm = getSecurityManager()

        # Add to the list of cacheable objects.
        for subob in subobs:
            if not isCacheable(subob):
                continue
            associated = (subob.ZCacheable_getManagerId() == manager_id)
            if require_assoc and not associated:
                continue
            if not sm.checkPermission('Change cache settings', subob):
                continue
            subpath = path + (subob.getId(),)
            icon = getattr(aq_base(subob), 'icon', '')
            info = {
                'sortkey': subpath,
                'path': '/'.join(subpath),
                'title': getattr(aq_base(subob), 'title', ''),
                'icon': icon,
                'associated': associated,}
            rval.append(info)

        # Visit subfolders.
        if subfolders:
            if meta_types:
                subobs = ob.objectValues()
            for subob in subobs:
                subpath = path + (subob.getId(),)
                if hasattr(aq_base(subob), 'objectValues'):
                    if sm.checkPermission(
                        'Access contents information', subob):
                        findCacheables(
                            subob, manager_id, require_assoc,
                            subfolders, meta_types, rval, subpath)
    except:
        # Ignore exceptions.
        import traceback
        traceback.print_exc()


class Cache:
    '''
    A base class (and interface description) for caches.
    Note that Cache objects are not intended to be visible by
    restricted code.
    '''

    def ZCache_invalidate(self, ob):
        raise 'Not implemented'

    def ZCache_get(self, ob, view_name, keywords, mtime_func, default):
        # view_name: If an object provides different views that would
        #   benefit from caching, it will set view_name.
        #   Otherwise view_name will be an empty string.
        #
        # keywords: Either None or a mapping containing keys that
        #   distinguish this cache entry from others even though
        #   ob and view_name are the same.  DTMLMethods use keywords
        #   derived from the DTML namespace.
        #
        # mtime_func: When the Cache calls ZCacheable_getModTime(),
        #   it should pass this as an argument.  It is provided to
        #   allow cacheable objects to provide their own computation
        #   of the object's modification time.
        #
        # default: If no entry is found, ZCache_get() should return
        #   default.
        raise 'Not implemented'

    def ZCache_set(self, ob, data, view_name, keywords, mtime_func):
        # See ZCache_get() for parameter descriptions.
        raise 'Not implemented'


class CacheManager:
    '''
    A base class for cache managers.  Implement ZCacheManager_getCache().
    '''

    ZCacheManager_getCache__roles__ = ()
    def ZCacheManager_getCache(self):
        raise 'Not implemented'

    _isCacheManager = 1

    __ac_permissions__ = (
        ('Change cache settings', ('ZCacheManager_locate',
                                   'ZCacheManager_setAssociations',
                                   'ZCacheManager_associate'),
         ('Manager',)),
        )

    manage_options = (
        {'label':'Associate',
         'action':'ZCacheManager_associate',
         'help':('OFSP','CacheManager-associate.stx'),
         },
        )

    def manage_afterAdd(self, item, container):
        # Adds self to the list of cache managers in the container.
        if aq_base(self) is aq_base(item):
            ids = getVerifiedManagerIds(container)
            id = self.getId()
            if id not in ids:
                setattr(container, ZCM_MANAGERS, ids + (id,))
                global manager_timestamp
                manager_timestamp = time.time()

    def manage_beforeDelete(self, item, container):
        # Removes self from the list of cache managers.
        if aq_base(self) is aq_base(item):
            ids = getVerifiedManagerIds(container)
            id = self.getId()
            if id in ids:
                setattr(container, ZCM_MANAGERS, filter(
                    lambda s, id=id: s != id, ids))
                global manager_timestamp
                manager_timestamp = time.time()

    ZCacheManager_associate = DTMLFile('dtml/cmassoc', globals())

    def ZCacheManager_locate(self, require_assoc, subfolders,
                             meta_types=[], REQUEST=None):
        '''Locates cacheable objects.
        '''
        ob = aq_parent(aq_inner(self))
        rval = []
        manager_id = self.getId()
        if '' in meta_types:
            # User selected "All".
            meta_types = []
        findCacheables(ob, manager_id, require_assoc, subfolders,
                       meta_types, rval, ())
        if REQUEST is not None:
            return self.ZCacheManager_associate(
                self, REQUEST, show_results=1, results=rval,
                management_view="Associate")
        else:
            return rval

    def ZCacheManager_setAssociations(self, props=None, REQUEST=None):
        '''Associates and un-associates cacheable objects with this
        cache manager.
        '''
        addcount = 0
        remcount = 0
        parent = aq_parent(aq_inner(self))
        sm = getSecurityManager()
        my_id = str(self.getId())
        if props is None:
            props = REQUEST.form
        for key, do_associate in props.items():
            if key[:10] == 'associate_':
                path = key[10:]
                ob = parent.restrictedTraverse(path)
                if not sm.checkPermission('Change cache settings', ob):
                    raise Unauthorized
                if not isCacheable(ob):
                    # Not a cacheable object.
                    continue
                manager_id = str(ob.ZCacheable_getManagerId())
                if do_associate:
                    if manager_id != my_id:
                        ob.ZCacheable_setManagerId(my_id)
                        addcount = addcount + 1
                else:
                    if manager_id == my_id:
                        ob.ZCacheable_setManagerId(None)
                        remcount = remcount + 1
        if REQUEST is not None:
            return self.ZCacheManager_associate(
                self, REQUEST, management_view="Associate",
                manage_tabs_message='%d association(s) made, %d removed.'%
                (addcount, remcount)
                )

Globals.default__class_init__(CacheManager)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.