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()
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)
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)
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)