[Erp5-report] r41598 seb - in /erp5/trunk/products/CMFActivity: ./ tests/

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Dec 21 14:54:38 CET 2010


Author: seb
Date: Tue Dec 21 14:54:37 2010
New Revision: 41598

URL: http://svn.erp5.org?rev=41598&view=rev
Log:
Due to the sorting of database connectors during a transaction, the
ZMySQLDA connector for the activities database would finish its
commit procedure before ZODB, making the description of an activity
message in MySQL available before its respective data in the ZODB.

The fix consisted in replacing the ZMySQLDA connector with another
one based on ZMySQLDA but with a “sortKey()” method that forced it
to be sorted after both the ZODB connection and the ZMySQLDA
connection for ZSQLCatalog.

Analysis of issue was done by Sebastien and Julien.

This patch itself was done by Leonardo.

Added:
    erp5/trunk/products/CMFActivity/ActivityConnection.py
Modified:
    erp5/trunk/products/CMFActivity/ActivityTool.py
    erp5/trunk/products/CMFActivity/__init__.py
    erp5/trunk/products/CMFActivity/tests/testCMFActivity.py

Added: erp5/trunk/products/CMFActivity/ActivityConnection.py
URL: http://svn.erp5.org/erp5/trunk/products/CMFActivity/ActivityConnection.py?rev=41598&view=auto
==============================================================================
--- erp5/trunk/products/CMFActivity/ActivityConnection.py (added)
+++ erp5/trunk/products/CMFActivity/ActivityConnection.py [utf8] Tue Dec 21 14:54:37 2010
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
+#                    Leonardo Rochael Almeida <leonardo at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+
+from Products.ZMySQLDA.DA import Connection
+from Products.ERP5Type.Globals import InitializeClass
+from App.special_dtml import HTMLFile
+
+# If the sort order below doesn't work, we cannot guarantee the setSortKey()
+# call below will actually result in the activity connection being committed
+# after the ZODB and Catalog data.
+assert None < 0 < '' < (), "Cannot guarantee commit of activities comes after the appropriate data"
+
+manage_addActivityConnectionForm = HTMLFile('dtml/connectionAdd', globals())
+
+def manage_addActivityConnection(self, id, title,
+                                 connection_string,
+                                 check=None, REQUEST=None):
+    """Add a DB connection to a folder"""
+    self._setObject(id, Connection(id, title, connection_string, check))
+    if REQUEST is not None: return self.manage_main(self,REQUEST)
+
+class ActivityConnection(Connection):
+    """Products ZMySQLDA.DA.Connection subclass that tweaks the sortKey() of
+       the actual connection to commit after all other connections
+    """
+    meta_type = title = 'CMFActivity Database Connection'
+
+    # Declarative constructors
+    constructors = (manage_addActivityConnectionForm,
+                    manage_addActivityConnection)
+
+    # reuse the permission from ZMySQLDA
+    permission_type = 'Add Z MySQL Database Connections'
+
+    def connect(self, s):
+        result = Connection.connect(self, s)
+        # the call above will set self._v_database_connection, and it won't
+        # have disappeared by now.
+        self._v_database_connection.setSortKey( (0,) )
+        return result
+
+InitializeClass(ActivityConnection)

Modified: erp5/trunk/products/CMFActivity/ActivityTool.py
URL: http://svn.erp5.org/erp5/trunk/products/CMFActivity/ActivityTool.py?rev=41598&r1=41597&r2=41598&view=diff
==============================================================================
--- erp5/trunk/products/CMFActivity/ActivityTool.py [utf8] (original)
+++ erp5/trunk/products/CMFActivity/ActivityTool.py [utf8] Tue Dec 21 14:54:37 2010
@@ -37,6 +37,7 @@ from Products.CMFCore import permissions
 from Products.ERP5Type.Core.Folder import Folder
 from Products.CMFActivity.ActiveResult import ActiveResult
 from Products.CMFActivity.ActiveObject import DEFAULT_ACTIVITY
+from Products.CMFActivity.ActivityConnection import ActivityConnection
 from Products.PythonScripts.Utility import allow_class
 from AccessControl import ClassSecurityInfo, Permissions
 from AccessControl.SecurityManagement import newSecurityManager
@@ -45,8 +46,7 @@ from AccessControl.SecurityManagement im
 from AccessControl.SecurityManagement import getSecurityManager
 from Products.CMFCore.utils import UniqueObject, _getAuthenticatedUser, getToolByName
 from Products.ERP5Type.Globals import InitializeClass, DTMLFile
-from Acquisition import aq_base
-from Acquisition import aq_inner
+from Acquisition import aq_base, aq_inner, aq_parent
 from ActivityBuffer import ActivityBuffer
 from ActivityRuntimeEnvironment import BaseMessage
 from zExceptions import ExceptionFormatter
@@ -547,14 +547,29 @@ class ActivityTool (Folder, UniqueObject
                 meta_types.append(meta_type)
         return meta_types
 
+    def maybeMigrateConnectionClass(self):
+      connection_id = 'cmf_activity_sql_connection'
+      sql_connection = getattr(self, connection_id, None)
+      if (sql_connection is not None and
+          not isinstance(sql_connection, ActivityConnection)):
+        # SQL Connection migration is needed
+        LOG('ActivityTool', WARNING, "Migrating MySQL Connection class")
+        parent = aq_parent(aq_inner(sql_connection))
+        parent._delObject(sql_connection.getId())
+        new_sql_connection = ActivityConnection(connection_id,
+                                                sql_connection.title,
+                                                sql_connection.connection_string)
+        parent._setObject(connection_id, new_sql_connection)
+
     def initialize(self):
       global is_initialized
       from Activity import RAMQueue, RAMDict, SQLQueue, SQLDict
       # Initialize each queue
       for activity in activity_dict.itervalues():
         activity.initialize(self)
+      self.maybeMigrateConnectionClass()
       is_initialized = True
-      
+
     security.declareProtected(Permissions.manage_properties, 'isSubscribed')
     def isSubscribed(self):
         """

Modified: erp5/trunk/products/CMFActivity/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/CMFActivity/__init__.py?rev=41598&r1=41597&r2=41598&view=diff
==============================================================================
--- erp5/trunk/products/CMFActivity/__init__.py [utf8] (original)
+++ erp5/trunk/products/CMFActivity/__init__.py [utf8] Tue Dec 21 14:54:37 2010
@@ -40,8 +40,10 @@ document_classes = updateGlobals(this_mo
 # Finish installation
 def initialize( context ):
   # Define object classes and tools
-  import ActivityTool, ActiveProcess
-  object_classes = (ActiveProcess.ActiveProcess, )
+  import ActivityTool, ActiveProcess, ActivityConnection
+  object_classes = (ActiveProcess.ActiveProcess,
+                    #ActivityConnection.ActivityConnection
+                    )
   portal_tools = (ActivityTool.ActivityTool, )
   content_classes = ()
   content_constructors = ()
@@ -51,6 +53,15 @@ def initialize( context ):
                     content_constructors=content_constructors,
                     content_classes=content_classes)
 
+  # register manually instead of using object_classes above so we can reuse
+  # the ZMySQLDA icon without having to carry the gif around in our own product
+  context.registerClass(
+        ActivityConnection.ActivityConnection,
+        permission='Add Z MySQL Database Connections', # reuse the permission
+        constructors=(ActivityConnection.manage_addActivityConnectionForm,
+                      ActivityConnection.manage_addActivityConnection),
+  )
+
 # This is used by a script (external method) that can be run
 # to set up CMFActivity in an existing CMF Site instance.
 cmfactivity_globals = globals()

Modified: erp5/trunk/products/CMFActivity/tests/testCMFActivity.py
URL: http://svn.erp5.org/erp5/trunk/products/CMFActivity/tests/testCMFActivity.py?rev=41598&r1=41597&r2=41598&view=diff
==============================================================================
--- erp5/trunk/products/CMFActivity/tests/testCMFActivity.py [utf8] (original)
+++ erp5/trunk/products/CMFActivity/tests/testCMFActivity.py [utf8] Tue Dec 21 14:54:37 2010
@@ -39,6 +39,7 @@ from Products.CMFActivity.ActiveObject i
                                               VALIDATE_ERROR_STATE
 from Products.CMFActivity.Activity.Queue import VALIDATION_ERROR_DELAY
 from Products.CMFActivity.Activity.SQLDict import SQLDict
+import Products.CMFActivity.ActivityTool
 from Products.CMFActivity.Errors import ActivityPendingError, ActivityFlushError
 from erp5.portal_type import Organisation
 from AccessControl.SecurityManagement import newSecurityManager
@@ -3827,6 +3828,51 @@ class TestCMFActivity(ERP5TypeTestCase, 
     finally:
       del activity_tool.__class__.doSomething
 
+  def test_connection_migration(self):
+    """
+    Make sure the cmf_activity_sql_connection is automatically migrated from
+    the ZMySQLDA Connection class to ActivityConnection
+    """
+    # replace the activity connector with a standard ZMySQLDA one
+    portal = self.portal
+    activity_tool = portal.portal_activities
+    stdconn = self.portal.cmf_activity_sql_connection
+    portal._delObject('cmf_activity_sql_connection')
+    portal.manage_addProduct['ZMySQLDA'].manage_addZMySQLConnection(
+        stdconn.id,
+        stdconn.title,
+        stdconn.connection_string,
+    )
+    oldconn = portal.cmf_activity_sql_connection
+    self.assertEquals(oldconn.meta_type, 'Z MySQL Database Connection')
+    # de-initialize and check that migration of the connection happens
+    # automatically
+    Products.CMFActivity.ActivityTool.is_initialized = False
+    activity_tool.activate(activity='SQLQueue').getId()
+    self.tic()
+    newconn = portal.cmf_activity_sql_connection
+    self.assertEquals(newconn.meta_type, 'CMFActivity Database Connection')
+
+  def test_connection_installable(self):
+    """
+    Test if the cmf_activity_sql_connector can be installed
+    """
+    # delete the activity connection
+    portal = self.portal
+    activity_tool = portal.portal_activities
+    stdconn = self.portal.cmf_activity_sql_connection
+    portal._delObject('cmf_activity_sql_connection')
+    # check the installation form can be rendered
+    portal.manage_addProduct['CMFActivity'].connectionAdd(
+        portal.REQUEST
+    )
+    # check it can be installed
+    portal.manage_addProduct['CMFActivity'].manage_addActivityConnection(
+        stdconn.id,
+        stdconn.title,
+        stdconn.connection_string
+    )
+
 def test_suite():
   suite = unittest.TestSuite()
   suite.addTest(unittest.makeSuite(TestCMFActivity))



More information about the Erp5-report mailing list