2 * File: SuperDataStore.h
4 * Author: Lukas Zeller (luz@synthesis.ch)
7 * "Virtual" datastore consisting of an union of other
8 * datastores, for example a vCal datastore based on
9 * two separate vEvent and vTodo datastores.
11 * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
13 * 2002-08-05 : luz : created
18 #include "prefix_file.h"
20 #include "syncappbase.h"
21 #include "superdatastore.h"
23 #ifdef SUPERDATASTORES
26 using namespace sysync;
29 // sub-datastore link config
30 // =========================
34 TSubDSLinkConfig::TSubDSLinkConfig(TLocalDSConfig *aLocalDSConfigP, TConfigElement *aParentElementP) :
35 TConfigElement(aLocalDSConfigP->getName(),aParentElementP)
38 fLinkedDSConfigP=aLocalDSConfigP;
39 } // TSubDSLinkConfig::TSubDSLinkConfig
43 TSubDSLinkConfig::~TSubDSLinkConfig()
46 } // TSubDSLinkConfig::~TSubDSLinkConfig
50 void TSubDSLinkConfig::clear(void)
53 fDispatchFilter.erase();
57 } // TSubDSLinkConfig::clear
60 #ifndef HARDCODED_CONFIG
62 // remote rule config element parsing
63 bool TSubDSLinkConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
65 // checking the elements
66 // - identification of remote
67 if (strucmp(aElementName,"dispatchfilter")==0)
68 expectString(fDispatchFilter);
69 else if (strucmp(aElementName,"guidprefix")==0)
70 expectString(fGUIDPrefix);
73 return inherited::localStartElement(aElementName,aAttributes,aLine);
76 } // TSubDSLinkConfig::localStartElement
81 // superdatastore config
82 // =====================
84 TSuperDSConfig::TSuperDSConfig(const char* aName, TConfigElement *aParentElement) :
85 TLocalDSConfig(aName,aParentElement)
88 } // TSuperDSConfig::TSuperDSConfig
91 TSuperDSConfig::~TSuperDSConfig()
94 } // TSuperDSConfig::~TSuperDSConfig
98 void TSuperDSConfig::clear(void)
101 // - no datastore links
102 TSubDSConfigList::iterator pos;
103 for(pos=fSubDatastores.begin();pos!=fSubDatastores.end();pos++)
105 fSubDatastores.clear();
108 } // TSuperDSConfig::clear
111 #ifndef HARDCODED_CONFIG
113 // config element parsing
114 bool TSuperDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
116 // checking the elements
117 // - links to sub-datastores
118 if (strucmp(aElementName,"contains")==0) {
119 // definition of a new datastore
120 const char* nam = getAttr(aAttributes,"datastore");
122 return fail("'contains' missing 'datastore' attribute");
123 // search sub-datastore
124 TLocalDSConfig *subdscfgP =
125 static_cast<TSessionConfig *>(getParentElement())->getLocalDS(nam);
127 return fail("unknown datastore '%s' specified",nam);
128 // create new datastore link
129 TSubDSLinkConfig *dslinkcfgP =
130 new TSubDSLinkConfig(subdscfgP,this);
132 fSubDatastores.push_back(dslinkcfgP);
133 // - let element handle parsing
134 expectChildParsing(*dslinkcfgP);
138 return TLocalDSConfig::localStartElement(aElementName,aAttributes,aLine);
141 } // TSuperDSConfig::localStartElement
146 void TSuperDSConfig::localResolve(bool aLastPass)
149 // check for required settings
153 inherited::localResolve(aLastPass);
154 } // TSuperDSConfig::localResolve
157 // - create appropriate datastore from config, calls addTypeSupport as well
158 TLocalEngineDS *TSuperDSConfig::newLocalDataStore(TSyncSession *aSessionP)
160 // Synccap defaults to normal set supported by the engine by default
161 TSuperDataStore *sdsP =
162 new TSuperDataStore(this,aSessionP,getName(),aSessionP->getSyncCapMask());
164 addTypes(sdsP,aSessionP);
166 } // TLocalDSConfig::newLocalDataStore
173 * Implementation of TSuperDataStore
176 /* public TSuperDataStore members */
179 TSuperDataStore::TSuperDataStore(TSuperDSConfig *aDSConfigP, TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask) :
180 TLocalEngineDS(aDSConfigP, aSessionP, aName, aCommonSyncCapMask)
183 fDSConfigP = aDSConfigP;
185 SYSYNC_THROW(TSyncException(DEBUGTEXT("TSuperDataStore::TSuperDataStore called with NULL config","lds1")));
187 InternalResetDataStore();
188 // create links to subdatastores
189 TSubDSConfigList::iterator pos;
190 TSubDatastoreLink link;
191 for(pos=aDSConfigP->fSubDatastores.begin();pos!=aDSConfigP->fSubDatastores.end();pos++) {
192 // start not yet pending
193 link.fStartPending=false;
194 // set link to subdatastore link config
195 link.fDSLinkConfigP=*pos;
196 // find actual datastore by "handle" (= config pointer)
197 link.fDatastoreLinkP=aSessionP->findLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
198 // if actual datastore does not yet exist, create it now. This can be
199 // the case for clients, where datastores are only instantiated when
200 // directly addressed by a SyncRequest (which will not happen for
201 // subdatastores normally)
202 if (!link.fDatastoreLinkP) {
204 link.fDatastoreLinkP=aSessionP->addLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
207 fSubDSLinks.push_back(link);
209 // Important: We need to get the iterator now again, as the implicit
210 // iterator init via InternalResetDataStore() is invalid because the list was empty then.
211 fCurrentGenDSPos=fSubDSLinks.begin();
212 } // TSuperDataStore::TSuperDataStore
215 void TSuperDataStore::InternalResetDataStore(void)
218 fFirstTimeSync=false;
219 TSubDSLinkList::iterator pos;
220 // cancel all pending starts
221 for(pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
222 pos->fStartPending=false;
224 fSuperStartPending = false;
225 // make sure this is set in case startSync() is not called before generateSyncCommands()
226 fCurrentGenDSPos=fSubDSLinks.begin();
227 } // TSuperDataStore::InternalResetDataStore
230 TSuperDataStore::~TSuperDataStore()
232 InternalResetDataStore();
233 } // TSuperDataStore::~TSuperDataStore
237 // Session events, which need some distribution to subdatastores
238 // =============================================================
240 // Methods overriding TLocalEngineDS
241 // ----------------------------------
244 // obtain Sync Cap mask, must be lowest common mask of all subdatastores
245 uInt32 TSuperDataStore::getSyncCapMask(void)
247 // AND of all subdatastores
248 uInt32 capmask = ~0; // all bits set
249 TSubDSLinkList::iterator pos;
250 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
251 capmask = capmask & pos->fDatastoreLinkP->getSyncCapMask();
254 } // TSuperDataStore::getSyncCapMask
257 // process Sync alert from remote party: check if alert code is supported,
258 // check if slow sync is needed due to anchor mismatch
259 // - server case: also generate appropriate Alert acknowledge command
260 TAlertCommand *TSuperDataStore::engProcessSyncAlert(
261 TSuperDataStore *aAsSubDatastoreOf, // if acting as subdatastore
262 uInt16 aAlertCode, // the alert code
263 const char *aLastRemoteAnchor, // last anchor of client
264 const char *aNextRemoteAnchor, // next anchor of client
265 const char *aTargetURI, // target URI as sent by remote, no processing at all
266 const char *aIdentifyingTargetURI, // target URI that was used to identify datastore
267 const char *aTargetURIOptions, // option string contained in target URI
268 SmlFilterPtr_t aTargetFilter, // DS 1.2 filter, NULL if none
269 const char *aSourceURI, // source URI
270 TStatusCommand &aStatusCommand // status that might be modified
273 TAlertCommand *alertcmdP=NULL;
275 TAlertCommand *subalertcmdP=NULL;
276 TStatusCommand substatus(fSessionP);
279 // alert all subdatastores
280 TSubDSLinkList::iterator pos;
281 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
282 subalertcmdP=pos->fDatastoreLinkP->engProcessSyncAlert(
283 this, // as subdatastore of myself
284 aAlertCode, // the alert code
285 aLastRemoteAnchor, // last anchor of client
286 aNextRemoteAnchor, // next anchor of client
287 aTargetURI, // target URI as sent by remote, no processing at all
288 aIdentifyingTargetURI, // target URI (without possible CGI)
289 aTargetURIOptions, // filtering CGI (NULL or empty if none)
290 aTargetFilter, // DS 1.2 filter, NULL if none
291 aSourceURI, // source URI
292 substatus // status that might be modified
295 // get rid of this, we don't need it
299 // basic problem with one of the subdatastores
300 // - propagate error code
301 aStatusCommand.setStatusCode(substatus.getStatusCode());
305 // this one is pending for start
306 pos->fStartPending=true;
308 // set flag to indicate this subdatastore has init pending
309 // Now all subdatastores should be successfully alerted and have current anchor infos ready,
310 // so we can call inherited (which will obtain combined anchors from our logicInitSyncAnchors)
311 alertcmdP = inherited::engProcessSyncAlert(
312 aAsSubDatastoreOf, // as indicated by caller (normally, superdatastore is not subdatastore of another superdatastore, but...)
313 aAlertCode, // the alert code
314 aLastRemoteAnchor, // last anchor of client
315 aNextRemoteAnchor, // next anchor of client
316 aTargetURI, // target URI as sent by remote, no processing at all
317 aIdentifyingTargetURI, // target URI (without possible CGI)
318 aTargetURIOptions, // filtering CGI (NULL or empty if none)
319 aTargetFilter, // DS 1.2 filter, NULL if none
320 aSourceURI, // source URI
321 aStatusCommand // status that might be modified
323 // entire superdatastore is pending for start
324 fSuperStartPending=true;
327 // clean up locally owned objects
328 if (alertcmdP) delete alertcmdP;
329 if (subalertcmdP) delete subalertcmdP;
333 } // TSuperDataStore::engProcessSyncAlert
336 // process status received for sync alert
337 bool TSuperDataStore::engHandleAlertStatus(TSyError aStatusCode)
339 // show it to all subdatastores
340 TSubDSLinkList::iterator pos;
341 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
342 pos->fDatastoreLinkP->engHandleAlertStatus(aStatusCode);
344 // all subdatastores have seen the alert status, so let superdatastore handle it as well
345 return TLocalEngineDS::engHandleAlertStatus(aStatusCode);
346 } // TSuperDataStore::engHandleAlertStatus
349 // Set remote datastore for local
350 void TSuperDataStore::engSetRemoteDatastore(
351 TRemoteDataStore *aRemoteDatastoreP // the remote datastore involved
354 // set all subdatastores to (same) remote datastore as superdatastore itself
355 TSubDSLinkList::iterator pos;
356 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
357 pos->fDatastoreLinkP->engSetRemoteDatastore(aRemoteDatastoreP);
359 // set in superdatastore as well
360 TLocalEngineDS::engSetRemoteDatastore(aRemoteDatastoreP);
361 } // TSuperDataStore::engSetRemoteDatastore
364 // set Sync types needed for sending local data to remote DB
365 void TSuperDataStore::setSendTypeInfo(
366 TSyncItemType *aLocalSendToRemoteTypeP,
367 TSyncItemType *aRemoteReceiveFromLocalTypeP
370 // set all subdatastores to (same) types as superdatastore itself
371 TSubDSLinkList::iterator pos;
372 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
373 pos->fDatastoreLinkP->setSendTypeInfo(aLocalSendToRemoteTypeP,aRemoteReceiveFromLocalTypeP);
375 // set in superdatastore as well
376 TLocalEngineDS::setSendTypeInfo(aLocalSendToRemoteTypeP,aRemoteReceiveFromLocalTypeP);
377 } // TSuperDataStore::setSendTypeInfo
380 // set Sync types needed for receiving remote data in local DB
381 void TSuperDataStore::setReceiveTypeInfo(
382 TSyncItemType *aLocalReceiveFromRemoteTypeP,
383 TSyncItemType *aRemoteSendToLocalTypeP
386 // set all subdatastores to (same) types as superdatastore itself
387 TSubDSLinkList::iterator pos;
388 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
389 pos->fDatastoreLinkP->setReceiveTypeInfo(aLocalReceiveFromRemoteTypeP,aRemoteSendToLocalTypeP);
391 // set in superdatastore as well
392 TLocalEngineDS::setReceiveTypeInfo(aLocalReceiveFromRemoteTypeP,aRemoteSendToLocalTypeP);
393 } // TSuperDataStore::setReceiveTypeInfo
396 // init usage of datatypes set with setSendTypeInfo/setReceiveTypeInfo
397 localstatus TSuperDataStore::initDataTypeUse(void)
399 localstatus sta=LOCERR_OK;
401 // set all subdatastores to (same) types as superdatastore itself
402 TSubDSLinkList::iterator pos;
403 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
404 sta = pos->fDatastoreLinkP->initDataTypeUse();
406 return sta; // failed
408 // set in superdatastore as well
409 return TLocalEngineDS::initDataTypeUse();
410 } // TSuperDataStore::initDataTypeUse
413 // SYNC command bracket start (check credentials if needed)
414 bool TSuperDataStore::engProcessSyncCmd(
415 SmlSyncPtr_t aSyncP, // the Sync element
416 TStatusCommand &aStatusCommand, // status that might be modified
417 bool &aQueueForLater // will be set if command must be queued for later (re-)execution
420 // start sync for all subdatastores
423 TSubDSLinkList::iterator pos;
424 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
425 // Note: this will cause subdatastores to call their own startSync,
426 // so we do NOT need to iterate over subdatastores in our startSync!
428 // if in init phase (entire superdatastore pending to start)
429 // only call subdatastores that are still pending for start, too
431 if (!fSuperStartPending || pos->fStartPending) {
432 ok=pos->fDatastoreLinkP->engProcessSyncCmd(aSyncP,aStatusCommand,doqueue);
434 // this one is now initialized. Do not do it again until all others are initialized, too
435 pos->fStartPending=false;
438 // we must queue the entire command for later (but some subdatastores might be excluded then)
439 aQueueForLater=true; // queue if one of the subdatastores needs it
442 if (!ok) return false;
445 ok=TLocalEngineDS::engProcessSyncCmd(aSyncP,aStatusCommand,doqueue);
446 if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
447 // if we reach this w/o queueing, start is no longer pending
448 if (!aQueueForLater) fSuperStartPending=false;
451 } // TSuperDataStore::processSyncCmd
454 // SYNC command bracket end (but another might follow in next message)
455 bool TSuperDataStore::engProcessSyncCmdEnd(bool &aQueueForLater)
457 // signal sync end to all subdatastores
460 TSubDSLinkList::iterator pos;
461 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
463 ok=pos->fDatastoreLinkP->engProcessSyncCmdEnd(doqueue);
464 if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
465 if (!ok) return false;
467 // signal it to myself
468 ok=TLocalEngineDS::engProcessSyncCmdEnd(doqueue);
469 if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
471 } // TSuperDataStore::engProcessSyncCmdEnd
477 localstatus TSuperDataStore::engProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
479 TSubDatastoreLink *linkP = NULL;
480 localstatus sta = LOCERR_OK;
482 // item has local ID, we can find datastore by prefix
483 linkP = findSubLinkByLocalID(aLocalID);
485 sta = 404; // not found
488 // let subdatastore process (and only show subDS part of localID)
489 sta=linkP->fDatastoreLinkP->engProcessMap(
491 aLocalID+linkP->fDSLinkConfigP->fGUIDPrefix.size()
495 } // TSuperDataStore::engProcessMap
497 #endif // SYSYNC_SERVER
500 // called to process incoming item operation
501 // Method takes ownership of syncitemP in all cases
502 // - returns true (and unmodified or non-200-successful status) if
503 // operation could be processed regularily
504 // - returns false (but probably still successful status) if
505 // operation was processed with internal irregularities, such as
506 // trying to delete non-existant item in datastore with
507 // incomplete Rollbacks (which returns status 200 in this case!).
508 bool TSuperDataStore::engProcessRemoteItem(
509 TSyncItem *syncitemP,
510 TStatusCommand &aStatusCommand
516 TSyncItem *itemcopyP;
521 "SuperProcessItem", "Processing incoming item in superdatastore",
522 "datastore=%s|SyncOp=%s|RemoteID=%s|LocalID=%s",
524 SyncOpNames[syncitemP->getSyncOp()],
525 syncitemP->getRemoteID(),
526 syncitemP->getLocalID()
528 // let appropriate subdatastore handle the command
529 TSubDatastoreLink *linkP = NULL;
530 TSyncOperation sop=syncitemP->getSyncOp();
532 TSubDSLinkList::iterator pos;
537 case sop_wants_replace:
541 // item has no local ID, we need to apply filters to item data
542 PDEBUGPRINTFX(DBG_DATA,("Checkin subdatastore filters to find where it belongs"));
543 linkP = findSubLinkByData(*syncitemP);
544 if (!linkP) goto nods;
545 PDEBUGPRINTFX(DBG_DATA,(
546 "Found item belongs to subdatastore '%s'",
547 linkP->fDatastoreLinkP->getName()
549 // make sure item does not have a local ID (which would be wrong because of prefixes anyway)
550 syncitemP->clearLocalID();
551 // remembert because we might need it below for move-replace
552 remid=syncitemP->getRemoteID();
553 // let subdatastore process
554 regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
555 // now check if replace was treated as add, if yes, this indicates
556 // that this might be a move between subdatastores
558 (sop==sop_replace || sop==sop_wants_replace) &&
559 !fSlowSync && aStatusCommand.getStatusCode()==201
561 // this is probably a move from another datastore by changing an attribute
562 // that dispatches datastores (such as a vEvent changed to a vToDo)
563 // - so we delete all items with this remote ID in all other datastores
564 PDEBUGPRINTFX(DBG_DATA,("Replace could be a move between subdatastores, trying to delete all items with same remoteID in other subdatastores"));
565 TStatusCommand substatus(fSessionP);
566 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
567 if (&(*pos) != linkP) {
568 // all but original datastore
569 substatus.setStatusCode(200);
570 itemcopyP = new TSyncItem();
571 // - only remote ID and syncop are relevant, leave everything else empty
572 itemcopyP->setRemoteID(remid.c_str());
573 itemcopyP->setSyncOp(sop_delete);
574 // - now try to delete. This might fail if replace above wasn't a move
575 // itemcopyP is consumed
576 PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
577 "Trying to delete item with remoteID='%s' from subdatastore '%s'",
578 itemcopyP->getRemoteID(),
579 linkP->fDatastoreLinkP->getName()
581 regular=pos->fDatastoreLinkP->engProcessRemoteItem(itemcopyP,substatus);
585 PDEBUGPRINTFX(DBG_DATA,(
586 "Found item in '%s', deleted here (and moved to '%s')",
587 pos->fDatastoreLinkP->getName(),
588 linkP->fDatastoreLinkP->getName()
594 PDEBUGPRINTFX(DBG_DATA,("End of (possible) move-replace between subdatastores"));
595 regular=true; // fully ok, no matter if delete above has succeeded or not
598 case sop_archive_delete:
599 case sop_soft_delete:
602 // item has no local ID AND no data, only a remoteID:
603 // we must try to read item from all subdatastores by remoteID until
605 // get an empty item of correct type to call logicRetrieveItemByID
606 itemcopyP = getLocalReceiveType()->newSyncItem(getRemoteSendType(),this);
607 // - only remote ID is relevant, leave everything else empty
608 itemcopyP->setRemoteID(syncitemP->getRemoteID());
609 // try to read item from all subdatastores
610 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
612 // always start with 200
613 aStatusCommand.setStatusCode(200);
615 PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
616 "Trying to read item by remoteID='%s' from subdatastore '%s' to see if it is there",
617 itemcopyP->getRemoteID(),
618 linkP->fDatastoreLinkP->getName()
620 regular=linkP->fDatastoreLinkP->logicRetrieveItemByID(*itemcopyP,aStatusCommand);
621 // must be ok AND not 404 (item not found)
622 if (regular && aStatusCommand.getStatusCode()!=404) {
623 PDEBUGPRINTFX(DBG_DATA,(
624 "Item found in subdatastore '%s', deleting it there",
625 linkP->fDatastoreLinkP->getName()
627 // now we can delete or copy, consuming original item
628 regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
629 // delete duplicated item as well
636 // none of the datastores could process this item --> error
637 // - delete duplicated item
639 // - make sure delete reports 200 for incomplete-rollback-datastores
640 if (aStatusCommand.getStatusCode()==404 && sop!=sop_copy) {
641 // not finding an item for delete might be ok for remote...
642 if (fSessionP->getSessionConfig()->fDeletingGoneOK) {
643 // 404/410: item not found, could be because previous aborted session has
644 // already committed deletion of that item -> behave as if delete was ok
645 PDEBUGPRINTFX(DBG_DATA,("to-be-deleted item was not found, but do NOT report %hd",aStatusCommand.getStatusCode()));
646 aStatusCommand.setStatusCode(200);
648 // ...but it is a internal irregularity, fall thru to return false
650 // is an internal irregularity
653 case sop_reference_only:
656 case numSyncOperations:
657 // nothing to do or shouldn't happen
661 #endif // SYSYNC_SERVER
666 case sop_wants_replace:
668 case sop_archive_delete:
669 case sop_soft_delete:
672 // item has local ID, we can find datastore by prefix
673 linkP = findSubLinkByLocalID(syncitemP->getLocalID());
674 if (!linkP) goto nods;
675 // remove prefix before letting subdatastore process it
676 syncitemP->fLocalID.erase(0,linkP->fDSLinkConfigP->fGUIDPrefix.size());
677 // now let subdatastore process
678 regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
682 // item has no local ID, we need to apply filters to item data
683 linkP = findSubLinkByData(*syncitemP);
684 if (!linkP) goto nods;
685 // make sure item does not have a local ID (which would be wrong because of prefixes anyway)
686 syncitemP->clearLocalID();
687 // let subdatastore process
688 regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
690 case sop_reference_only:
693 case numSyncOperations:
694 // nothing to do or shouldn't happen
698 #endif // SYSYNC_CLIENT
700 // no datastore or unknown command, general DB error
701 aStatusCommand.setStatusCode(510);
702 PDEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::processRemoteItem Fatal: Item cannot be processed by any subdatastore"));
708 PDEBUGENDBLOCK("SuperProcessItem");
710 } // TSuperDataStore::engProcessRemoteItem
714 // - must return true if this datastore is finished with <sync>
715 // (if all datastores return true,
716 // session is allowed to finish sync packet with outgoing message
717 bool TSuperDataStore::isSyncDone(void)
719 // check subdatastores
721 TSubDSLinkList::iterator pos;
722 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
723 done=done && pos->fDatastoreLinkP->isSyncDone();
726 return done && TLocalEngineDS::isSyncDone();
727 } // TSuperDataStore::isSyncDone
730 // abort sync with this super datastore (that is, with all subdatastores as well)
731 void TSuperDataStore::engAbortDataStoreSync(TSyError aStatusCode, bool aLocalProblem, bool aResumable)
733 // abort subdatastores
734 TSubDSLinkList::iterator pos;
735 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
736 pos->fDatastoreLinkP->engAbortDataStoreSync(aStatusCode,aLocalProblem,aResumable);
738 // set code in my own ancestor
739 TLocalEngineDS::engAbortDataStoreSync(aStatusCode,aLocalProblem,aResumable);
740 } // TSuperDataStore::engAbortDataStoreSync
743 // - must return true if this datastore is finished with <sync>
744 // (if all datastores return true,
745 // session is allowed to finish sync packet with outgoing message
746 bool TSuperDataStore::isAborted(void)
748 // check subdatastores
749 TSubDSLinkList::iterator pos;
750 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
751 if (pos->fDatastoreLinkP->isAborted()) return true; // one aborted, super aborted as well
754 return TLocalEngineDS::isAborted();
755 } // TSuperDataStore::isAborted
758 // called at very end of sync session, when everything is done
759 // Note: is also called before deleting a datastore (so aborted sessions
760 // can do cleanup and/or statistics display as well)
761 void TSuperDataStore::engFinishDataStoreSync(localstatus aErrorStatus)
763 // inform all subdatastores
764 TSubDSLinkList::iterator pos;
765 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
766 pos->fDatastoreLinkP->engFinishDataStoreSync(aErrorStatus);
769 inherited::engFinishDataStoreSync(aErrorStatus);
770 } // TSuperDataStore::engFinishDataStoreSync
773 // Internal events during sync to access local database
774 // ====================================================
776 // Methods overriding TLocalEngineDS
777 // ----------------------------------
780 // Abstracts of TLocalEngineDS
781 // ----------------------------
784 // called at sync alert (before generating for client, after receiving for server)
785 // - obtains combined anchor from subdatastores
786 // - combines them into a common anchor (if possible)
787 // - updates fFirstTimeSync as well
788 localstatus TSuperDataStore::engInitSyncAnchors(
789 cAppCharP aDatastoreURI, // local datastore URI
790 cAppCharP aRemoteDBID // ID of remote datastore (to find session information in local DB)
793 bool allanchorsequal=true;
794 localstatus sta=LOCERR_OK;
796 // superdatastore has no own anchors, so collect data from subdatastores
797 fFirstTimeSync=false;
798 TSubDSLinkList::iterator pos;
799 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
800 if (pos==fSubDSLinks.begin()) {
801 /* not needed, because engInitSyncAnchors() will be called only after all subdatastore's
802 engProcessSyncAlert() was called, which in turn contains a call to engInitSyncAnchors()
803 This means we can safely assume we have the fLastRemoteAnchor/fNextLocalAnchor info
805 In fact, calling this here AGAIN had the effect that the state of the first
806 subdatastore would be set BACK to dssta_adminready (instead of the required dssta_syncmodestable,
807 and in turn when the first sync command arrived, it would be rejected with "SYNC received too early".
808 // init anchors of subdatastore
809 sta = pos->fDatastoreLinkP->engInitSyncAnchors(aDatastoreURI, aRemoteDBID);
811 break; // exit, we cannot init
813 // assign references of first datastore
814 // - this must be same from all subdatastores
815 fLastRemoteAnchor=pos->fDatastoreLinkP->fLastRemoteAnchor;
816 // - these are used from the first datastore, and might differ (a few seconds,
817 // that is) for other datastores
818 fLastLocalAnchor=pos->fDatastoreLinkP->fLastLocalAnchor;
819 fNextLocalAnchor=pos->fDatastoreLinkP->fNextLocalAnchor;
822 // see if all are equal
823 allanchorsequal = allanchorsequal &&
824 pos->fDatastoreLinkP->fLastRemoteAnchor == fLastRemoteAnchor;
826 // also combine firstTimeSync (first time if it's first for any of the subdatastores)
827 fFirstTimeSync = fFirstTimeSync || pos->fDatastoreLinkP->fFirstTimeSync;
829 // make sure common anchor is valid only if all of the subdatastores have equal anchors
830 if (sta!=LOCERR_OK || fFirstTimeSync || !allanchorsequal) {
831 fLastLocalAnchor.empty();
832 fLastRemoteAnchor.empty();
834 // superdatastore gets adminready when all subdatastores have successfully done engInitSyncAnchors()
835 if (sta==LOCERR_OK) {
836 changeState(dssta_adminready); // admin data is now ready
840 } // TSuperDataStore::engInitSyncAnchors
843 // - called at start of first <Sync> command (prepare DB for reading/writing)
844 bool TSuperDataStore::startSync(TStatusCommand &aStatusCommand)
846 DEBUGPRINTFX(DBG_HOT,("TSuperDataStore::startSync"));
847 // make sure we start generating with first datastore
848 fCurrentGenDSPos=fSubDSLinks.begin();
849 // NOTE: Do NOT iterate subdatastores, because these were called already
850 // by engProcessSyncCmd (server case) or engProcessSyncAlert (client case)
852 } // TSuperDataStore::startSync
855 /// check is datastore is completely started.
856 /// @param[in] aWait if set, call will not return until either started state is reached
857 /// or cannot be reached within the maximally allowed request processing time left.
858 bool TSuperDataStore::engIsStarted(bool aWait)
860 // check subdatastores
862 TSubDSLinkList::iterator pos;
863 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
864 ready=ready && pos->fDatastoreLinkP->engIsStarted(aWait);
867 return ready && inherited::engIsStarted(aWait);
868 } // TSuperDataStore::engIsStarted
871 // remove prefix for given subDatastore
872 // @param[in] aIDWithPrefix points to ID with prefix
873 // @return NULL if either datastore not found or prefix not present in aIDWithPrefix
874 // @return pointer to first char in aIDWithPrefix which is not part of the prefix
875 cAppCharP TSuperDataStore::removeSubDSPrefix(cAppCharP aIDWithPrefix, TLocalEngineDS *aLocalDatastoreP)
877 if (!aIDWithPrefix) return NULL;
878 TSubDSLinkList::iterator pos;
879 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
880 if (pos->fDatastoreLinkP == aLocalDatastoreP) {
884 pos->fDSLinkConfigP->fGUIDPrefix.c_str(),
885 pos->fDSLinkConfigP->fGUIDPrefix.size()
887 return aIDWithPrefix+pos->fDSLinkConfigP->fGUIDPrefix.size(); // return start of subDS ID
889 return aIDWithPrefix; // datastore found, but prefix is not there, return unmodified
893 } // TSuperDataStore::removeSubDSPrefix
896 // private helper: find subdatastore which matches prefix of given localID
897 TSubDatastoreLink *TSuperDataStore::findSubLinkByLocalID(const char *aLocalID)
899 TSubDSLinkList::iterator pos;
900 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
903 pos->fDSLinkConfigP->fGUIDPrefix.c_str(),
904 pos->fDSLinkConfigP->fGUIDPrefix.size()
910 return NULL; // not found
911 } // TSuperDataStore::findSubLinkByLocalID
914 // private helper: find subdatastore which can accept item data
915 TSubDatastoreLink *TSuperDataStore::findSubLinkByData(TSyncItem &aSyncItem)
917 TSubDSLinkList::iterator pos;
918 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
919 PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
920 "Testing item data against <dispatchfilter> of subdatastore '%s'",
921 pos->fDatastoreLinkP->getName()
923 if (aSyncItem.testFilter(pos->fDSLinkConfigP->fDispatchFilter.c_str())) {
928 return NULL; // not found
929 } // TSuperDataStore::findSubLinkByData
932 // only dummy, creates error if called
933 bool TSuperDataStore::logicRetrieveItemByID(
934 TSyncItem &aSyncItem, // item to be filled with data from server. Local or Remote ID must already be set
935 TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
938 aStatusCommand.setStatusCode(500);
939 DEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::logicRetrieveItemByID called, which should never happen!!!!!!"));
940 return false; // not ok
941 } // TSuperDataStore::logicRetrieveItemByID
944 // only dummy, creates error if called
945 // - Method takes ownership of syncitemP in all cases
946 bool TSuperDataStore::logicProcessRemoteItem(
947 TSyncItem *syncitemP,
948 TStatusCommand &aStatusCommand,
949 bool &aVisibleInSyncset, // on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
950 string *aGUID // GUID is stored here if not NULL
953 delete syncitemP; // consume
954 aStatusCommand.setStatusCode(500);
955 DEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::logicProcessRemoteItem called, which should never happen!!!!!!"));
956 return false; // not ok
957 } // TSuperDataStore::logicProcessRemoteItem
960 // - returns true if DB implementation can filter during database fetch
961 // (otherwise, fetched items must be filtered after being read from DB)
962 bool TSuperDataStore::engFilteredFetchesFromDB(bool aFilterChanged)
964 // only if all subdatastores support it
966 TSubDSLinkList::iterator pos;
967 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
968 yes = yes && pos->fDatastoreLinkP->engFilteredFetchesFromDB(aFilterChanged);
971 } // TSuperDataStore::engFilteredFetchesFromDB
974 // - called for SyncML 1.1 if remote wants number of changes.
975 // Must return -1 if no NOC value can be returned
976 sInt32 TSuperDataStore::getNumberOfChanges(void)
978 sInt32 noc,totalNoc = 0;
979 TSubDSLinkList::iterator pos;
980 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
981 noc = pos->fDatastoreLinkP->getNumberOfChanges();
982 if (noc<0) return -1; // if one of the subdatastores does not know NOC, we can't return a NOC
983 // subdatastore knows its NOC, sum up
986 // return sum of all NOCs
988 }; // TSuperDataStore::getNumberOfChanges
991 // show statistics or error of current sync
992 void TSuperDataStore::showStatistics(void)
994 // show something in debug log
995 PDEBUGPRINTFX(DBG_HOT,("Superdatastore Sync for '%s' (%s), %s sync status:",
997 fRemoteViewOfLocalURI.c_str(),
998 fSlowSync ? "slow" : "normal"
1000 // and on user console
1001 CONSOLEPRINTF((""));
1002 CONSOLEPRINTF(("- Superdatastore Sync for '%s' (%s), %s sync status:",
1004 fRemoteViewOfLocalURI.c_str(),
1005 fSlowSync ? "slow" : "normal"
1010 PDEBUGPRINTFX(DBG_ERROR,("Warning: Failed with status code=%hd",fAbortStatusCode));
1011 CONSOLEPRINTF((" ************ Failed with status code=%hd",fAbortStatusCode));
1014 // successful: show statistics on console
1015 PDEBUGPRINTFX(DBG_HOT,("Completed successfully - details see subdatastores"));
1016 CONSOLEPRINTF((" Completed successfully - details see subdatastores"));
1018 CONSOLEPRINTF((""));
1019 } // TSuperDataStore::showStatistics
1022 // - returns true if DB implementation of all subdatastores support resume
1023 bool TSuperDataStore::dsResumeSupportedInDB(void)
1025 // yes if all subdatastores support it
1027 TSubDSLinkList::iterator pos;
1028 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1029 yes = yes && pos->fDatastoreLinkP->dsResumeSupportedInDB();
1032 } // TSuperDataStore::dsResumeSupportedInDB
1035 // helper to save resume state either at end of request or explicitly at reception of a "suspend"
1036 localstatus TSuperDataStore::engSaveSuspendState(bool aAnyway)
1038 // only save here if not aborted already (aborting saves the state immediately)
1039 // or explicitly requested
1040 if (aAnyway || !isAborted()) {
1041 // only save if DS 1.2 and supported by DB
1042 if ((fSessionP->getSyncMLVersion()>=syncml_vers_1_2) && dsResumeSupportedInDB()) {
1043 PDEBUGBLOCKFMT(("SuperSaveSuspendState","Saving superdatastore suspend/resume state","superdatastore=%s",getName()));
1045 fResumeAlertCode=fAlertCode;
1046 TSubDSLinkList::iterator pos;
1047 if (fResumeAlertCode) {
1048 // let all subdatastores update partial item and markOnlyUngeneratedForResume()
1049 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1050 // save partial state if any
1051 if (pos->fDatastoreLinkP->fPartialItemState!=pi_state_save_outgoing) {
1052 // ONLY if we have no request for saving an outgoing item state already,
1053 // we possibly need to save a pending incoming item
1054 // if there is an incompletely received item, let it update Partial Item (fPIxxx) state
1055 // (if it is an item of this datastore, that is).
1056 if (fSessionP->fIncompleteDataCommandP) {
1057 fSessionP->fIncompleteDataCommandP->updatePartialItemState(pos->fDatastoreLinkP);
1061 pos->fDatastoreLinkP->logicMarkOnlyUngeneratedForResume();
1063 /// @note that already generated items are related to the originating
1064 /// localEngineDS, so markPendingForResume() on existing commands will
1065 /// directly reach the correct datastore
1066 /// @note markItemForResume() will get the localID as presented to
1067 /// remote, that is in case of superdatastores with prefixes that need to be removed
1068 fSessionP->markPendingForResume(this);
1070 // let all subdatastores logicSaveResumeMarks() to make all this persistent
1071 localstatus globErr=LOCERR_OK;
1072 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1073 localstatus err=pos->fDatastoreLinkP->logicSaveResumeMarks();
1074 if (err!=LOCERR_OK) globErr=err;
1076 PDEBUGENDBLOCK("SuperSaveSuspendState");
1081 } // TSuperDataStore::engSaveSuspendState
1084 #ifdef SYSYNC_SERVER
1086 /// @brief called at end of request processing, should be used to save suspend state
1087 /// @note subdatastores don't do anything themselves, to make sure superds can make things happen in correct order
1088 void TSuperDataStore::engRequestEnded(void)
1090 // variant for superdatastore - also handles its subdatastores
1091 // For DS 1.2: Make sure everything is ready for a resume in case there's an abort (implicit Suspend)
1092 // before the next request. Note that the we cannot wait for session timeout, as the resume attempt
1093 // from the client probably arrives much earlier.
1094 if (testState(dssta_syncmodestable)) {
1095 // make sure all unsent items are marked for resume
1096 localstatus sta=engSaveSuspendState(false); // only if not already aborted
1097 if (sta!=LOCERR_OK) {
1098 DEBUGPRINTFX(DBG_ERROR,("Could not save suspend state at end of Request: err=%hd",sta));
1101 // let datastore prepare for end of request (other than thread change)
1102 TSubDSLinkList::iterator pos;
1103 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1104 pos->fDatastoreLinkP->dsRequestEnded();
1106 // then let them know that thread may change
1107 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1108 pos->fDatastoreLinkP->dsThreadMayChangeNow();
1110 } // TSuperDataStore::engRequestEnded
1115 // - called to let server generate sync commands for client
1116 // Returns true if now finished for this datastore
1117 // also sets fState to dss_syncdone when finished
1118 bool TSuperDataStore::engGenerateSyncCommands(
1119 TSmlCommandPContainer &aNextMessageCommands,
1120 TSmlCommand * &aInterruptedCommandP,
1121 const char *aLocalIDPrefix
1124 PDEBUGBLOCKFMT(("SuperSyncGen","Now generating sync commands from superdatastore","datastore=%s",getName()));
1125 bool finished=false;
1128 while (!isAborted()) {
1130 if (fCurrentGenDSPos==fSubDSLinks.end()) {
1131 // done, update status
1132 changeState(dssta_syncgendone,true);
1135 // create current prefix
1136 AssignString(prefix,aLocalIDPrefix);
1137 prefix.append(fCurrentGenDSPos->fDSLinkConfigP->fGUIDPrefix);
1138 // call subdatastore to generate commands
1139 finished=fCurrentGenDSPos->fDatastoreLinkP->engGenerateSyncCommands(
1140 aNextMessageCommands,
1141 aInterruptedCommandP,
1144 // exit if not yet finished with generating commands for this datastore
1145 if (!finished) break;
1146 // done with this datastore, switch to next if any
1148 } // while not aborted
1149 // finished when state is dss_syncdone
1150 PDEBUGPRINTFX(DBG_DATA,(
1151 "superdatastore's engGenerateSyncCommands ended, state='%s', sync generation %sdone",
1153 dbgTestState(dssta_syncgendone,true) ? "" : "NOT "
1155 PDEBUGENDBLOCK("SuperSyncGen");
1156 // also finished with this datastore when aborted
1157 return (isAborted() || testState(dssta_syncgendone,true));
1158 } // TSuperDataStore::generateSyncCommands
1161 #ifdef SYSYNC_CLIENT
1163 // Client only: returns number of unsent map items
1164 sInt32 TSuperDataStore::numUnsentMaps(void)
1166 // add maps from all subdatastores
1168 TSubDSLinkList::iterator pos;
1169 for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1170 num+=pos->fDatastoreLinkP->numUnsentMaps();
1173 } // TSuperDataStore::numUnsentMaps
1176 // called to mark maps confirmed, that is, we have received ok status for them
1177 void TSuperDataStore::engMarkMapConfirmed(cAppCharP aLocalID, cAppCharP aRemoteID)
1179 // we must detect the subdatastore by prefix
1180 TSubDatastoreLink *linkP = findSubLinkByLocalID(aLocalID);
1182 // pass to subdatastore with prefix removed
1183 linkP->fDatastoreLinkP->engMarkMapConfirmed(aLocalID+linkP->fDSLinkConfigP->fGUIDPrefix.size(),aRemoteID);
1185 } // TSuperDataStore::engMarkMapConfirmed
1188 // - client only: called to generate Map items
1189 // Returns true if now finished for this datastore
1190 // also sets fState to dss_done when finished
1191 bool TSuperDataStore::engGenerateMapItems(TMapCommand *aMapCommandP, cAppCharP aLocalIDPrefix)
1193 TSubDSLinkList::iterator pos=fSubDSLinks.begin();
1197 PDEBUGBLOCKDESC("SuperMapGenerate","TSuperDataStore: Generating Map items...");
1199 // check if already done
1200 if (pos==fSubDSLinks.end()) break; // done
1201 // create current prefix
1202 AssignString(prefix,aLocalIDPrefix);
1203 prefix.append(fCurrentGenDSPos->fDSLinkConfigP->fGUIDPrefix);
1204 // generate Map items
1205 ok=pos->fDatastoreLinkP->engGenerateMapItems(aMapCommandP,prefix.c_str());
1206 // exit if not yet finished with generating map items for this datastore
1208 PDEBUGENDBLOCK("MapGenerate");
1209 return false; // not all map items generated
1215 // we are done if state is syncdone (no more sync commands will occur)
1216 if (testState(dssta_dataaccessdone)) {
1217 changeState(dssta_clientmapssent,true);
1218 PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished generating Map items, server has finished <sync>, we are done now"))
1221 // else if we are not yet dssta_syncgendone -> this is the end of a early pending map send
1222 else if (!dbgTestState(dssta_syncgendone)) {
1223 PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished sending cached Map items from last session"))
1225 // otherwise, we are not really finished with the maps yet (but with the current map command)
1227 PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished generating Map items for now, but server still sending <Sync>"))
1230 PDEBUGENDBLOCK("MapGenerate");
1232 } // TSuperDataStore::engGenerateMapItems
1235 #endif // SYSYNC_CLIENT
1238 /* end of TSuperDataStore implementation */
1240 #endif // SUPERDATASTORES