Commit 78cb52bc authored by Aurel's avatar Aurel

implement deletion of object (works for asynchronous engine only for now)

parent de2b6575
...@@ -97,8 +97,7 @@ class SyncMLSubscription(XMLObject): ...@@ -97,8 +97,7 @@ class SyncMLSubscription(XMLObject):
self._edit(authenticated_user=None) self._edit(authenticated_user=None)
security.declarePrivate('getAndActivate') security.declarePrivate('getAndActivate')
def getAndActivate(self, callback, method_kw, activate_kw, def getAndActivate(self, callback, method_kw, activate_kw, **kw):
final_activate_kw, **kw):
""" """
This methods is called by the asynchronous engine to split activity This methods is called by the asynchronous engine to split activity
generation into activities. generation into activities.
...@@ -106,7 +105,6 @@ class SyncMLSubscription(XMLObject): ...@@ -106,7 +105,6 @@ class SyncMLSubscription(XMLObject):
callback : method to call in activity callback : method to call in activity
method_kw : callback's parameters method_kw : callback's parameters
activate_kw : activity parameters to pass to activate call activate_kw : activity parameters to pass to activate call
final_activate_kw : activity parameters to pass to the last activate call
kw : any parameter getAndActivate can required if it calls itself kw : any parameter getAndActivate can required if it calls itself
Last activate must wait for all other activities to be processed in order Last activate must wait for all other activities to be processed in order
...@@ -141,7 +139,7 @@ class SyncMLSubscription(XMLObject): ...@@ -141,7 +139,7 @@ class SyncMLSubscription(XMLObject):
(kw["min_id"],)) (kw["min_id"],))
self.activate(**next_kw).getAndActivate( self.activate(**next_kw).getAndActivate(
callback, method_kw, activate_kw, final_activate_kw, **kw) callback, method_kw, activate_kw, **kw)
generated_other_activity = True generated_other_activity = True
r = [x.getId() for x in r] r = [x.getId() for x in r]
message_id_list = self.getNextMessageIdList(id_count=result_count) message_id_list = self.getNextMessageIdList(id_count=result_count)
...@@ -166,25 +164,12 @@ class SyncMLSubscription(XMLObject): ...@@ -166,25 +164,12 @@ class SyncMLSubscription(XMLObject):
activate_kw=activate_kw, activate_kw=activate_kw,
**method_kw) **method_kw)
# Final activity must be executed after all other # Final activity must be executed after all other
callback_method = getattr(activate(**final_activate_kw), callback) syncml_logger.info("---- getAndActivate : final call for %s : %s" %(r[i+packet_size:], activate_kw))
syncml_logger.info("---- getAndActivate : final call for %s : %s" %(r[i+packet_size:], final_activate_kw))
callback_method(id_list=r[i+packet_size:], # XXX Has to be unit tested callback_method(id_list=r[i+packet_size:], # XXX Has to be unit tested
# with mock object # with mock object
message_id=message_id_list.pop(), message_id=message_id_list.pop(),
activate_kw=final_activate_kw, activate_kw=activate_kw,
is_final_message=True,
**method_kw) **method_kw)
else:
# We got no more result, but we must send a message with a final tag
activate = self.getPortalObject().portal_synchronizations.activate
callback_method = getattr(activate(**final_activate_kw), callback)
syncml_logger.info("getAndActivate : No result : final call")
callback_method(id_list=[],
message_id=self.getNextMessageId(),
activate_kw=final_activate_kw,
is_final_message=True,
**method_kw)
return result_count return result_count
security.declarePrivate('sendMessage') security.declarePrivate('sendMessage')
...@@ -482,26 +467,18 @@ class SyncMLSubscription(XMLObject): ...@@ -482,26 +467,18 @@ class SyncMLSubscription(XMLObject):
def _getDeletedData(self, syncml_response): def _getDeletedData(self, syncml_response):
""" """
Return list of deleted data base on validation state of signature Add delete command to syncml resposne
""" XXX Delete signature from database when we receive confirmation message
return None # XXX Do nothing for now, must introduce a new state to distinguisehd """
# from not-synchronized object for which we wait for a status answer # XXX not efficient at all, must be review later
# Maybe signature can be make "synchronized" without the status id_list = [x.getId() for x in self.contentValues() if x.getValidationState() == "not_synchronized"]
# and changed if status not ok for gid in id_list:
deleted_signature_list = self.searchFolder( syncml_response.addDeleteCommand(gid=gid)
validation_state="not_synchronized")
# Slow method
# deleted_signature_list = [x for x in self.contentValues() if x.getValidationState() == "not_synchronized"]
syncml_logger.info("Got %d deleted objects list in %s" % (
len(deleted_signature_list), self.getPath(),))
for signature in deleted_signature_list:
syncml_response.addDeleteCommand(gid=signature.getId(),)
def _getSyncMLData(self, syncml_response, id_list=None): def _getSyncMLData(self, syncml_response, id_list=None):
""" """
XXX Comment to be fixed XXX Comment to be fixed
if object is not None, this usually means we want to set the
actual xupdate on the signature.
""" """
if not id_list: if not id_list:
syncml_logger.warning("Non optimal call to _getSyncMLData, no id list provided") syncml_logger.warning("Non optimal call to _getSyncMLData, no id list provided")
...@@ -531,7 +508,7 @@ class SyncMLSubscription(XMLObject): ...@@ -531,7 +508,7 @@ class SyncMLSubscription(XMLObject):
create_signature = alert_code != "refresh_from_client_only" create_signature = alert_code != "refresh_from_client_only"
if not len(object_list): if not len(object_list):
syncml_logger.warning("No object retrieved althoud id_list (%s) is provided" syncml_logger.warning("No object retrieved althoud id_list (%s) is provided"
% (id_list)) % (id_list))
for result in object_list: for result in object_list:
...@@ -586,6 +563,7 @@ class SyncMLSubscription(XMLObject): ...@@ -586,6 +563,7 @@ class SyncMLSubscription(XMLObject):
# The data will be copied in 'data' property once we get # The data will be copied in 'data' property once we get
# confirmation that the document was well synchronized # confirmation that the document was well synchronized
signature.setTemporaryData(document_data) signature.setTemporaryData(document_data)
signature.doSync()
# Generate the message # Generate the message
syncml_response.addSyncCommand( syncml_response.addSyncCommand(
...@@ -613,10 +591,8 @@ class SyncMLSubscription(XMLObject): ...@@ -613,10 +591,8 @@ class SyncMLSubscription(XMLObject):
sync_code='conflict_resolved_with_merge', sync_code='conflict_resolved_with_merge',
command='Replace') command='Replace')
set_synchronized = True
if not signature.checkMD5(xml_object): if not signature.checkMD5(xml_object):
# MD5 checksum tell there is a modification of the object # MD5 checksum tell there is a modification of the object
set_synchronized = False
if conduit.getContentType() != 'text/xml': if conduit.getContentType() != 'text/xml':
# If there is no xml, we re-send the whole object # If there is no xml, we re-send the whole object
# XXX this must be managed by conduit ? # XXX this must be managed by conduit ?
...@@ -661,11 +637,12 @@ class SyncMLSubscription(XMLObject): ...@@ -661,11 +637,12 @@ class SyncMLSubscription(XMLObject):
data=data_diff, data=data_diff,
more_data=more_data, more_data=more_data,
media_type=conduit.getContentType()) media_type=conduit.getContentType())
signature.doSync()
if set_synchronized and \ elif signature.getValidationState() != 'synchronized':
signature.getValidationState() != 'synchronized':
# We should not have this case when we are in CONFLICT_MERGE # We should not have this case when we are in CONFLICT_MERGE
signature.synchronize() signature.synchronize()
elif signature.getValidationState() == \ elif signature.getValidationState() == \
'conflict_resolved_with_client_command_winning': 'conflict_resolved_with_client_command_winning':
# We have decided to apply the update # We have decided to apply the update
...@@ -705,6 +682,9 @@ class SyncMLSubscription(XMLObject): ...@@ -705,6 +682,9 @@ class SyncMLSubscription(XMLObject):
more_data=more_data, more_data=more_data,
media_type=self.getContentType()) media_type=self.getContentType())
if not more_data:
signature.doSync()
if not more_data: if not more_data:
pass pass
#self.removeRemainingObjectPath(object_path) #self.removeRemainingObjectPath(object_path)
......
...@@ -218,7 +218,7 @@ class SyncMLAsynchronousEngine(EngineMixin): ...@@ -218,7 +218,7 @@ class SyncMLAsynchronousEngine(EngineMixin):
activity_created = self.runGetAndActivate(subscription=subscriber, activity_created = self.runGetAndActivate(subscription=subscriber,
after_method_id=after_method_id, after_method_id=after_method_id,
tag=tag) tag=tag)
syncml_logger.info("X--> Server is sending modifications in activities") syncml_logger.info("X--> Server is sending modifications in activities %s" %(activity_created))
if not activity_created: if not activity_created:
# Server has no modification to send to client, return final message # Server has no modification to send to client, return final message
syncml_logger.info("X-> Server sending final message") syncml_logger.info("X-> Server sending final message")
...@@ -257,14 +257,21 @@ class SyncMLAsynchronousEngine(EngineMixin): ...@@ -257,14 +257,21 @@ class SyncMLAsynchronousEngine(EngineMixin):
'subscription_path' : subscription.getRelativeUrl(), 'subscription_path' : subscription.getRelativeUrl(),
} }
pref = getSite().portal_preferences pref = getSite().portal_preferences
return subscription.getAndActivate( count = subscription.getAndActivate(
callback="sendSyncCommand", callback="sendSyncCommand",
method_kw=method_kw, method_kw=method_kw,
activate_kw=activate_kw, activate_kw=activate_kw,
final_activate_kw=final_activate_kw,
packet_size=pref.getPreferredDocumentRetrievedPerActivityCount(), packet_size=pref.getPreferredDocumentRetrievedPerActivityCount(),
activity_count=pref.getPreferredRetrievalActivityCount(), activity_count=pref.getPreferredRetrievalActivityCount(),
) )
# Then get deleted document
# this will act as the final message of this sync part
activate = subscription.getPortalObject().portal_synchronizations.activate
callback_method = getattr(activate(**activate_kw), "sendDeleteCommand")
callback_method(message_id=subscription.getNextMessageId(),
activate_kw=activate_kw,
**method_kw)
return True
def runApplySyncCommand(self, subscription, syncml_request, tag): def runApplySyncCommand(self, subscription, syncml_request, tag):
......
...@@ -463,4 +463,36 @@ class SynchronizationTool(BaseTool): ...@@ -463,4 +463,36 @@ class SynchronizationTool(BaseTool):
subscription.activate(**activate_kw).sendMessage(xml=str(syncml_response)) subscription.activate(**activate_kw).sendMessage(xml=str(syncml_response))
def sendDeleteCommand(self, message_id, subscription_path, activate_kw):
"""
This methods is intented to be called by asynchronous engine in activity to
send delete sync commands for a set of data
As engines are not zodb object, the tool acts as a placeholder for method
that need to be called in activities
"""
subscription = self.restrictedTraverse(subscription_path)
assert subscription is not None, "Impossible to find subscription %s" \
% (subscription_path)
# Build Message
syncml_response = SyncMLResponse()
syncml_response.addHeader(
session_id=subscription.getSessionId(),
message_id=message_id,
target=subscription.getUrlString(),
source=subscription.getSubscriptionUrlString())
syncml_response.addBody()
subscription._getDeletedData(
syncml_response=syncml_response,
)
# Notify that all modifications were sent
syncml_response.addFinal()
# Send the message in activity to prevent recomputing data in case of
# transport failure
subscription.activate(**activate_kw).sendMessage(xml=str(syncml_response))
InitializeClass(SynchronizationTool) InitializeClass(SynchronizationTool)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment