Пример #1
0
def autoRefresh(jar):
    # Must be called before there are any changes made
    # by the connection to the database!
    auto_refresh_ids = checkAutoRefresh(jar)
    if auto_refresh_ids:
        finishAutoRefresh(jar, auto_refresh_ids)
        from ZODB import Connection
        Connection.resetCaches()
        transaction.commit()
        jar._resetCache()
        transaction.begin()
Пример #2
0
def autoRefresh(jar):
    # Must be called before there are any changes made
    # by the connection to the database!
    auto_refresh_ids = checkAutoRefresh(jar)
    if auto_refresh_ids:
        finishAutoRefresh(jar, auto_refresh_ids)
        from ZODB import Connection
        Connection.resetCaches()
        transaction.commit()
        jar._resetCache()
        transaction.begin()
Пример #3
0
 def manage_performRefresh(self, REQUEST=None):
     '''
     Attempts to perform a refresh operation.
     '''
     if self._readRefreshTxt() is None:
         raise Unauthorized, 'refresh.txt not found'
     message = None
     if RefreshFuncs.performFullRefresh(self._p_jar, self.id):
         from ZODB import Connection
         Connection.resetCaches() # Clears cache in future connections.
         message = 'Product refreshed.'
     else:
         message = 'An exception occurred.'
     if REQUEST is not None:
         return self.manage_refresh(REQUEST, manage_tabs_message=message)
Пример #4
0
 def manage_performRefresh(self, REQUEST=None):
     """ Attempts to perform a refresh operation.
     """
     from App.RefreshFuncs import performFullRefresh
     if self._readRefreshTxt() is None:
         raise Unauthorized, 'refresh.txt not found'
     message = None
     if performFullRefresh(self._p_jar, self.id):
         from ZODB import Connection
         Connection.resetCaches()  # Clears cache in future connections.
         message = 'Product refreshed.'
     else:
         message = 'An exception occurred.'
     if REQUEST is not None:
         return self.manage_refresh(REQUEST, manage_tabs_message=message)
Пример #5
0
def synchronizeDynamicModules(context, force=False):
    """
  Allow resetting all classes to ghost state, most likely done after
  adding and removing mixins on the fly

  Most of the time, this reset is only hypothetic:
  * with force=False, the reset is only done if another node resetted
    the classes since the last reset on this node.
  * with force=True, forcefully reset the classes on the current node
    and send out an invalidation to other nodes
  """
    portal = context.getPortalObject()

    global last_sync
    if force:
        # hard invalidation to force sync between nodes
        portal.newCacheCookie('dynamic_classes')
        last_sync = portal.getCacheCookie('dynamic_classes')
    else:
        cookie = portal.getCacheCookie('dynamic_classes')
        if cookie == last_sync:
            # up to date, nothing to do
            return
        last_sync = cookie

    # Flush the entire ZODB.Connections pickle cache on next opening
    # (transaction beginning), for all connections.
    # There may be instances of the classes which are being reloaded in the
    # cache, and the code change may cause incompatible instance property
    # layouts. A very visible example is if the class does not exist prior to
    # the reload: if any instance was loaded, it is loaded as an instance of
    # the Broken class, which has a __setstate__ method which mangles
    # instance's properties. Then, post-reload the class cannot expect to
    # handle an instance with such property mangling, and will start behaving
    # in undefined ways.
    # Strictly, this issue also applies to any non-persistent instance of any
    # class defined in (or affected by) anything being reloaded. But as these
    # instances have not been made persistent, there is no guarantee that they
    # can be reloaded in any way.
    # Emptying the ZODB cache is the last thing short of restarting the whole
    # process.
    Connection.resetCaches()

    import erp5
    with aq_method_lock:
        # Thanks to TransactionalResource, the '_bootstrapped' global variable
        # is updated in a transactional way. Without it, it would be required to
        # restart the instance if anything went wrong.
        # XXX: In fact, TransactionalResource does not solve anything here, because
        #      portal cookie is unlikely to change and this function will return
        #      immediately, forcing the user to restart.
        #      This may not be so bad after all: it enables the user to do easily
        #      some changes that are required for the migration.
        if portal.id not in _bootstrapped and \
           TransactionalResource.registerOnce(__name__, 'bootstrap', portal.id):
            migrate = False
            from Products.ERP5Type.Tool.PropertySheetTool import PropertySheetTool
            from Products.ERP5Type.Tool.TypesTool import TypesTool
            from Products.ERP5Type.Tool.ComponentTool import ComponentTool
            from Products.ERP5.Tool.CategoryTool import CategoryTool
            from Products.ERP5Type.Tool.WorkflowTool import WorkflowTool
            from Products.ERP5Catalog.Tool.ERP5CatalogTool import ERP5CatalogTool
            try:
                for tool_class in TypesTool, PropertySheetTool, ComponentTool, ERP5CatalogTool, CategoryTool, WorkflowTool:
                    # if the instance has no property sheet tool, or incomplete
                    # property sheets, we need to import some data to bootstrap
                    # (only likely to happen on the first run ever)
                    tool_id = tool_class.id
                    tool = getattr(portal, tool_id, None)

                    if tool is None:
                        if tool_class == ERP5CatalogTool:
                            # Wait till we find that SQL Catalog Tool is installed
                            # Simpy said, we don't want  ERP5 Catalog Tool to be installed
                            # from here. So, we come to 2 cases:
                            # 1. Running ERP5Site with sql catalog_tool : In that case, we end up
                            # running _bootstrap from here, leading to migration.
                            # 2. New ERP5Site : In this case, we don't do anything here, cause
                            # the catalog_tool would be ERP5CatalogTool, so this would just pass.
                            continue
                        tool = tool_class()
                        portal._setObject(tool_id,
                                          tool,
                                          set_owner=False,
                                          suppress_events=True)
                        tool = getattr(portal, tool_id)
                    elif tool._isBootstrapRequired():
                        migrate = True
                    else:
                        continue
                    tool._bootstrap()
                    tool.__class__ = getattr(erp5.portal_type,
                                             tool.portal_type)
                # TODO: Create portal_activities here, and even before portal_types:
                #       - all code in ActiveObject could assume that it always exists
                #       - currently, some objects created very early are not indexed
                #         and this would fix this issue
                try:
                    portal.portal_activities.initialize()
                except AttributeError:
                    pass  # no Activity Tool yet

                for tool_id in ("portal_properties", "portal_uidannotation",
                                "portal_uidgenerator", "portal_uidhandler"):
                    if portal.hasObject(tool_id):
                        portal._delObject(tool_id, suppress_events=True)
                        migrate = True
                        if tool_id == 'portal_properties':
                            portal.portal_skins.erp5_xhtml_style.breadcrumbs.write(
                                'return []')

                if migrate:
                    portal.migrateToPortalTypeClass()
                    portal.portal_skins.changeSkin(None)
                    TransactionalResource(
                        tpc_finish=lambda txn: _bootstrapped.add(portal.id))
                    transaction.get().note('Site migrated')
                    LOG(
                        'ERP5Site', INFO,
                        'Transition successful, please update your'
                        ' business templates')
                else:
                    _bootstrapped.add(portal.id)

            except:
                # Required because the exception may be silently dropped by the caller.
                transaction.doom()
                LOG('ERP5Site',
                    PANIC,
                    "Automatic migration of core tools failed",
                    error=True)
                raise

        LOG("ERP5Type.dynamic", 0, "Resetting dynamic classes")
        try:
            for _, klass in inspect.getmembers(erp5.portal_type,
                                               inspect.isclass):
                # Zope Interface is implemented through __implements__,
                # __implemented__ (both implementedBy instances) and __provides__
                # (ClassProvides instance) attributes set on the class by
                # zope.interface.declarations.implementedByFallback.
                #
                # However both implementedBy and ClassProvides instances keep a
                # reference to the class itself, thus creating a circular references.
                for k in klass.mro():
                    module_name = k.__module__
                    if (module_name.startswith('erp5.') and
                            # Components are reset independently of Portal Types classes
                            not module_name.startswith('erp5.component.')):
                        for attr in ('__implements__', '__implemented__',
                                     '__provides__'):
                            if k.__dict__.get(attr) is not None:
                                delattr(k, attr)

                klass.restoreGhostState()

            # Clear accessor holders of ZODB Property Sheets and Portal Types
            erp5.accessor_holder.clear()
            erp5.accessor_holder.property_sheet.clear()

            for name in ensure_list(
                    erp5.accessor_holder.portal_type.__dict__.keys()):
                if name[0] != '_':
                    delattr(erp5.accessor_holder.portal_type, name)

        except Exception:
            # Allow easier debugging when the code is wrong as this
            # exception is catched later and re-raised as a BadRequest
            import traceback
            traceback.print_exc()
            raise

        # It's okay for classes to keep references to old methods - maybe.
        # but we absolutely positively need to clear the workflow chains
        # stored in WorkflowMethod objects: our generation of workflow
        # methods adds/registers/wraps existing methods, but does not
        # remove old chains. Do it now.
        resetRegisteredWorkflowMethod()

        # Some method generations are based on portal methods, and portal
        # methods cache results. So it is safer to invalidate the cache.
        cache_tool = getattr(portal, 'portal_caches', None)
        if cache_tool is not None:
            cache_tool.clearCache()

        # Clear Zope Component Registries (Zope Adapters/Utilities cache lookup)
        # because it contains references to reset dynamic classes (which prevents
        # them from being GC and may create inconsistencies when Interfaces have
        # been changed)
        import zope.component
        gsm = zope.component.getGlobalSiteManager()
        gsm.adapters.changed(gsm)
        gsm.utilities.changed(gsm)