4 * @Author Lukas Zeller (luz@synthesis.ch)
7 * Standard database logic implementation, suitable for most (currently all)
8 * actual DS implementations, but takes as few assumptions about datastore
9 * so for vastly different sync patterns, this could be replaced by differnt locic
11 * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
13 * @Date 2005-09-15 : luz : created from custdbdatastore
19 #include "prefix_file.h"
22 #include "stdlogicds.h"
23 #include "multifielditem.h"
24 #include "multifielditemtype.h"
26 using namespace sysync;
31 * Implementation of TStdLogicDS
34 /* public TStdLogicDS members */
37 TStdLogicDS::TStdLogicDS(
38 TLocalDSConfig *aDSConfigP,
39 sysync::TSyncSession *aSessionP,
41 long aCommonSyncCapMask
43 TLocalEngineDS(aDSConfigP, aSessionP, aName, aCommonSyncCapMask)
44 #ifdef MULTI_THREAD_DATASTORE
45 ,fStartSyncStatus(aSessionP) // a thread-private status command to store status ocurring during threaded startDataAccessForServer()
48 InternalResetDataStore();
49 fMultiThread= fSessionP->getSessionConfig()->fMultiThread; // get comfort pointer
50 } // TStdLogicDS::TStdLogicDS
53 TStdLogicDS::~TStdLogicDS()
55 InternalResetDataStore();
56 } // TStdLogicDS::~TStdLogicDS
59 void TStdLogicDS::InternalResetDataStore(void)
67 #ifdef MULTI_THREAD_DATASTORE // combined ifdef/flag
69 // make sure background processing aborts if it is in progress
70 fStartSyncThread.terminate(); // request soft termination of thread
71 if (!fStartSyncThread.waitfor()) {
72 // has not already terminated
73 PDEBUGPRINTFX(DBG_HOT,("******** Waiting for background thread to terminate"));
74 fStartSyncThread.waitfor(-1); // wait forever or until thread really terminates
75 PDEBUGPRINTFX(DBG_HOT,("******** Background thread terminated"));
79 // we are not initializing
81 // no start init request yet
83 #if !defined(SYSYNC_CLIENT) || defined(CLIENT_USES_SERVER_DB)
85 TSyncItemPContainer::iterator pos;
86 for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
89 fItems.clear(); // clear list
94 } // TStdLogicDS::InternalResetDataStore
97 // Internal events during sync to access local database
98 // ====================================================
101 // called to make admin data ready
102 localstatus TStdLogicDS::logicMakeAdminReady(cAppCharP aDataStoreURI, cAppCharP aRemoteDBID)
104 PDEBUGBLOCKFMTCOLL(("MakeAdminReady","Making Admin Data ready to check sync anchors","localDB=%s|remoteDB=%s",aDataStoreURI,aRemoteDBID));
105 // init local anchor strings, because it will not be set on impl level
106 // (impl level only uses timestamps for local anchor)
107 // These will be derived from timestamps below when implMakeAdminReady() is successful
108 fLastLocalAnchor.erase();
109 fNextLocalAnchor.erase();
110 // Updates the following state variables
111 // - from TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
112 // - for client: fPendingAddMaps
113 // - for server: fTempGUIDMap
114 // - from TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
115 // - from derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
116 localstatus sta = implMakeAdminReady(
117 fSessionP->getRemoteURI(), // remote device/server URI (device ID)
118 aDataStoreURI, // entire relative URI of local datastore
119 aRemoteDBID // database ID of remote device/server (=path as sent by remote)
121 if (sta==LOCERR_OK) {
122 // TStdLogicDS requires implementation to store timestamps to make the local anchors
123 // - regenerate last session's local anchor string from timestamp
124 TimestampToISO8601Str(fLastLocalAnchor,fPreviousSyncTime,TCTX_UTC,false,false);
125 // - create this session's local anchor string from timestamp
126 TimestampToISO8601Str(fNextLocalAnchor,fCurrentSyncTime,TCTX_UTC,false,false);
127 // - check if config has changed since last sync
128 if (fFirstTimeSync || fPreviousSyncTime<=fSessionP->getRootConfig()->fConfigDate) {
129 // remote should see our (probably changed) devInf
130 PDEBUGPRINTFX(DBG_PROTO,("First time sync or config changed since last sync -> remote should see our devinf"));
131 fSessionP->remoteMustSeeDevinf();
133 // empty saved anchors if first time sync (should be empty anyway, but...)
134 if (fFirstTimeSync) {
135 fLastLocalAnchor.empty();
136 fLastRemoteAnchor.empty();
139 PDEBUGENDBLOCK("MakeAdminReady");
141 } // TStdLogicDS::logicMakeAdminReady
145 // start writing if not already started
146 localstatus TStdLogicDS::startDataWrite()
148 localstatus sta = LOCERR_OK;
150 if (!fWriteStarted) {
151 sta=implStartDataWrite();
152 fWriteStarted = sta==LOCERR_OK; // must be here to prevent recursion as startDataWrite might be called implicitly below
153 #if !defined(SYSYNC_CLIENT) || defined(CLIENT_USES_SERVER_DB)
154 // server-type DB needs post-processing to update map entries (client and server case)
155 TSyncItemPContainer::iterator pos;
156 if (sta==LOCERR_OK) {
157 // Now allow post-processing of all reported items
158 for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
159 localstatus sta2 = implReviewReadItem(**pos);
160 if (sta2!=LOCERR_OK) sta=sta2;
164 #if defined(SYSYNC_CLIENT) && defined(CLIENT_USES_SERVER_DB)
165 /// @todo we don't need the items to remain that long at all - not even for CLIENT_USES_SERVER_DB case
166 fItems.clear(); // empty list
167 #endif // client using server DB
169 DEBUGPRINTFX(DBG_DATA,("startDataWrite called, status=%hd", sta));
171 } // TStdLogicDS::startDataWrite
175 localstatus TStdLogicDS::endDataWrite(void)
177 localstatus sta=LOCERR_OK;
179 DEBUGPRINTFX(DBG_DATA,(
180 "endDataWrite called, write %s started",
181 fWriteStarted ? "is" : "not"
183 // if we commit a write, the session is ok, and we can clear the resume state
185 sta = implEndDataWrite();
190 } // TStdLogicDS::endDataWrite
193 // - read specific item from database
194 // Data and missing ID information is filled in from local database
195 bool TStdLogicDS::logicRetrieveItemByID(
196 TSyncItem &aSyncItem, // item to be filled with data from server. Local or Remote ID must already be set
197 TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
200 // simply call implementation
201 return implRetrieveItemByID(aSyncItem,aStatusCommand);
202 } // TLocalEngineDS::logicRetrieveItemByID
205 /// check is datastore is completely started.
206 /// @param[in] aWait if set, call will not return until either started state is reached
207 /// or cannot be reached within the maximally allowed request processing time left.
208 bool TStdLogicDS::isStarted(bool aWait)
210 #ifndef SYSYNC_CLIENT
211 // only server has threaded datastores so far
212 if (aWait && fInitializing) {
213 localstatus sta = startDataAccessForServer();
214 if (sta!=LOCERR_OK) {
215 SYSYNC_THROW(TSyncException("startDataAccessForServer failed (when called from isStarted)", sta));
219 // if initialisation could not be completed in the first startDataAccessForServer() call
220 // we are not started.
221 return !fInitializing && inherited::isStarted(aWait);
222 } // TStdLogicDS::isStarted
225 /// called to mark an already generated (but probably not sent or not yet statused) item
226 /// as "to-be-resumed", by localID or remoteID (latter only in server case).
227 /// @note This must be repeatable without side effects, as server must mark/save suspend state
228 /// after every request (and not just at end of session)
229 void TStdLogicDS::logicMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent)
231 implMarkItemForResume(aLocalID, aRemoteID, aUnSent);
232 } // TStdLogicDS::logicMarkItemForResume
235 /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
236 /// error status conditions, by localID or remoteID (latter only in server case).
237 void TStdLogicDS::logicMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID)
239 implMarkItemForResend(aLocalID, aRemoteID);
240 } // TStdLogicDS::logicMarkItemForResend
244 #ifndef SYSYNC_CLIENT
249 // Actual start sync actions in DB. If server supports threaded init, this will
250 // be called in a sub-thread's context
251 localstatus TStdLogicDS::performStartSync(void)
253 localstatus sta = LOCERR_OK;
255 TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
256 sta = implStartDataRead();
258 if (sta==LOCERR_OK) {
259 // now get data from DB
260 if (!isRefreshOnly() || (isSlowSync() && isResuming())) {
261 // not only updating from client, so read all items now
262 // Note: for a resumed slow updating from client only, we need the
263 // currently present syncset as well as we need it to detect
267 PDEBUGBLOCKFMTCOLL(("GetItems","Read items from DB implementation","datastore=%s",getName()));
269 // check if external request to terminate loop
270 if (shouldExitStartSync()) {
271 PDEBUGPRINTFX(DBG_ERROR,("performStartSync aborted by external request"));
272 PDEBUGENDBLOCK("GetItems");
273 TP_START(fSessionP->fTPInfo,li);
277 TSyncItem *myitemP=NULL;
278 // report all items in syncset, not only changes if we need to filter
279 changed=!fFilteringNeededForAll; // let GetItem
280 // now fetch next item
281 sta = implGetItem(eof,changed,myitemP);
282 if (sta!=LOCERR_OK) {
283 implEndDataRead(); // terminate reading
284 PDEBUGENDBLOCK("GetItems");
285 TP_START(fSessionP->fTPInfo,li);
289 // read successful, test for eof
290 if (eof) break; // reading done
291 if (fSlowSync) changed=true; // all have changed (just in case GetItem does not return clean result here)
292 // NOTE: sop can be sop_reference_only ONLY in case of server resuming a slowsync
293 TSyncOperation sop=myitemP->getSyncOp();
294 // check if we need to do some filtering to determine final syncop
295 // NOTE: call postFetchFiltering even in case we do not actually
296 // need filtering (but we might need making item pass acceptance filter!)
297 if (sop!=sop_delete && sop!=sop_soft_delete && sop!=sop_archive_delete) {
298 // we need to post-fetch filter the item first
299 bool passes=postFetchFiltering(myitemP);
301 // item does not pass = does not belong to sync set per now
302 if (!fSlowSync && (sop==sop_wants_replace)) {
303 // item already exists on remote but falls out of syncset now: delete
304 // NOTE: This works only if reviewReadItem() is correctly implemented
305 // and checks for items that are deleted after being reported
306 // something else to delete their local map entry
308 myitemP->cleardata(); // also get rid of unneeded data
310 else sop=sop_none; // ignore all others (especially adds or slowsync replaces)
313 // item passes = belongs to sync set
314 if (sop==sop_wants_replace && !changed && !fSlowSync) {
315 // exists but has not changed since last sync
316 sop=sop_none; // ignore for now
320 // check if we should use that item
323 continue; // try next from DB
326 myitemP->setSyncOp(sop);
327 // %%% these are just-in-case tests for sloppy db interface
328 // - adjust operation for slowsync
330 if (sop==sop_delete || sop==sop_soft_delete || sop==sop_archive_delete) {
331 // do not process deleted items during slow sync at all
332 delete myitemP; // forget it
333 continue; // Read next item
336 // must be add or replace, will be an add by default (if unmatched)
337 // - set it to sop_wants_add to signal that this item was not matched yet!
338 myitemP->setRemoteID(""); // forget remote ID, is unknown in slow sync anyway
339 if (sop!=sop_reference_only) // if reference only (resumed slowsync), keep it as is
340 myitemP->setSyncOp(sop_wants_add); // flag it unmatched
343 // - now add it to my local list
344 fItems.push_back(myitemP);
345 if (sop==sop_reference_only)
346 fNumRefOnlyItems++; // count these to avoid them being shown in NOC
348 } while (true); // exit by break
349 PDEBUGENDBLOCK("GetItems");
350 } // not from client only
352 sta=implEndDataRead();
354 PDEBUGPRINTFX(DBG_HOT,("%s: number of local items involved in %ssync = %ld",getName(), fSlowSync ? "slow " : "",fItems.size()));
355 CONSOLEPRINTF((" %ld local items are new/changed/deleted for this sync",fItems.size()));
356 if (PDEBUGTEST(DBG_DATA+DBG_DETAILS)) {
357 PDEBUGBLOCKFMTCOLL(("SyncSet","Items involved in Sync","datastore=%s",getName()));
358 for (TSyncItemPContainer::iterator pos=fItems.begin();pos!=fItems.end();pos++) {
359 TSyncItem *syncitemP = (*pos);
360 PDEBUGPRINTFX(DBG_DATA,(
361 "SyncOp=%-20s: LocalID=%15s RemoteID=%15s",
362 SyncOpNames[syncitemP->getSyncOp()],
363 syncitemP->getLocalID(),
364 syncitemP->getRemoteID()
367 PDEBUGENDBLOCK("SyncSet");
369 // initiate writing and cause reviewing of items now,
370 // before any of them can be modified by sync process
372 // - startDataWrite() includes zapping the sync set
373 // in slow refresh only sessions (not resumed, see next note)
374 // - In case of resumed slow refresh from remote, the sync set
375 // will not get zapped again, because some items are already there
377 sta = startDataWrite(); // private helper
379 if (sta != LOCERR_OK) {
381 engAbortDataStoreSync(sta,true);
384 TP_START(fSessionP->fTPInfo,li);
386 } // startDataRead successful
388 TP_START(fSessionP->fTPInfo,li);
391 } // TStdLogicDS::performStartSync
394 #ifdef MULTI_THREAD_DATASTORE
396 // function executed by thread
397 static uInt32 StartSyncThreadFunc(TThreadObject *aThreadObject, uInt32 aParam)
399 // parameter passed is pointer to datastore
400 TStdLogicDS *datastoreP = static_cast<TStdLogicDS *>((void *)aParam);
401 // now call routine that actually performs datastore start
402 uInt32 exitCode = (uInt32) (datastoreP->performStartSync());
403 // thread is about to end (has ended for the Impl and Api levels), inform datastore
404 // and post necessary ThreadMayChangeNow() calls
405 datastoreP->endingThread();
408 } // StartSyncThreadFunc
411 bool TStdLogicDS::threadedStartSync(void)
413 // do this in a separate thread if requested
414 // Note: ThreadMayChangeNow() has been posted already by startingThread()
415 PDEBUGPRINTFX(DBG_HOT,("******* starting background thread for reading sync set..."));
416 fStartSyncStatus.setStatusCode(200); // assume ok
417 if (!fStartSyncThread.launch(StartSyncThreadFunc,(uInt32)this)) { // pass datastoreP as param
418 // starting thread failed
419 PDEBUGPRINTFX(DBG_ERROR,("******* Failed starting background thread for reading sync set"));
423 // show link to thread log
424 PDEBUGPRINTFX(DBG_HOT,(
425 "******* started &html;<a href=\"%s_%lu%s\" target=\"_blank\">&html;background thread id=%lu&html;</a>&html; for reading sync set",
426 getDbgLogger()->getDebugFilename(), // href base
427 fStartSyncThread.getid(), // plus thread
428 getDbgLogger()->getDebugExt(), // plus extension
429 fStartSyncThread.getid()
432 return true; // started ok
433 } // TStdLogicDS::threadedStartSync
436 // can be called to check if performStartSync() should be terminated
437 bool TStdLogicDS::shouldExitStartSync(void)
439 // threaded version, check if termination flag was set
440 return fMultiThread && fStartSyncThread.terminationRequested();
441 } // TStdLogicDS::shouldExitStartSync
445 // can be called to check if performStartSync() should be terminated
446 bool TStdLogicDS::shouldExitStartSync(void)
448 // nonthread version, is never the case
450 } // TStdLogicDS::shouldExitStartSync
455 // called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is
456 // getting ready for being accessed. Also called by isStarted(true) when starting
457 // up took longer than one request max time for threaded datastores
458 localstatus TStdLogicDS::startDataAccessForServer(void)
460 localstatus sta = LOCERR_OK;
462 DEBUGPRINTFX(DBG_HOT,("TStdLogicDS::startDataAccessForServer"));
463 if (!fInitializing) {
464 // The datastore has not started initializing yet
465 // - start initialisation now
467 // - read all records from DB right now if server data is used at all
468 DEBUGPRINTFX(DBG_DATA,("- number of items in list before StartDataRead = %ld",fItems.size()));
469 // now we can initialize the conflict resolution mode for this session
470 /// @todo move this to localengineds, at point where we get dssta_syncmodestable
471 fSessionConflictStrategy=getConflictStrategy(fSlowSync,fFirstTimeSync);
473 #ifdef SCRIPT_SUPPORT
474 // - call DB init script, which might add extra filters depending on options and remoterule
475 TScriptContext::execute(
476 fDataStoreScriptContextP,
477 getDSConfig()->fDBInitScript,
478 &DBFuncTable, // context's function table
479 this // datastore pointer needed for context
482 // - init post fetch filtering, sets fFilteringNeededForAll and fFilteringNeeded correctly
483 initPostFetchFiltering();
484 // - now we can start reading (fFilteringNeededForAll can be checked by StartDataRead)
485 fInitializing=true; // we enter the initialisation phase now
486 fStartInit=true; // and we want to start the init
488 // try starting init now (eventually repeats until it can be done)
490 PDEBUGPRINTFX(DBG_DATA,( "MultiThread %sabled", fMultiThread ? "en":"dis" ));
491 #ifdef MULTI_THREAD_DATASTORE // combined define and flag
494 if (startingThread()) {
495 // we may start a thread here
496 if (threadedStartSync())
497 fStartInit= false; // starting done now
502 // Just perform initialisation
503 sta = performStartSync();
504 fStartInit=false; // starting done now
505 fInitializing=false; // initialisation is already complete here
509 #ifdef MULTI_THREAD_DATASTORE // combined define and flag
511 // wait for started initialisation to finish within time we have left for this request
512 if (!fStartInit && fInitializing) {
513 // initialisation started but not ended so far: wait for it until done or defined request time passed
514 sInt32 t=fSessionP->RemainingRequestTime();
515 if (fStartSyncThread.waitfor(t<0 ? 0 : t * 1000)) {
516 // background thread has terminated
517 sta = fStartSyncThread.exitcode();
518 PDEBUGPRINTFX(DBG_HOT,("******* background thread for startSync() terminated with exit code=%ld, status sta=%hd", fStartSyncThread.exitcode(),sta));
519 // initialisation is now complete
526 // now complete initialisation if not still initializing in background
527 if (!fStartInit && !fInitializing) {
528 // finished background processing
529 if (sta==LOCERR_OK) {
530 // quick test: if number of items is > than allowed maxid of remote datatstore,
531 // sync is unlikely to succeed
532 if (getRemoteDatastore()->getMaxID()<fItems.size()) {
533 // this will not work, warn (but no longer abort session, as Siemens S55 guys don't like that)
535 "Warning: Synchronisation involves more items (%ld) than client can possibly manage (%ld",
536 (sInt32)fItems.size(),
537 (sInt32)getRemoteDatastore()->getMaxID()
539 PDEBUGPRINTFX(DBG_ERROR,(
540 "Warning: Synchronisation involves more items (%ld) than client can possibly manage (%ld)",
541 (sInt32)fItems.size(),
542 (sInt32)getRemoteDatastore()->getMaxID()
546 // return status of initialisation
550 // background processing still in progress
551 // - if we are still processing in background, init is ok so far
554 } // TStdLogicDS::startDataAccessForServer
557 // called to check if conflicting replace or delete command from server exists
558 TSyncItem *TStdLogicDS::getConflictingItemByRemoteID(TSyncItem *syncitemP)
560 // search for conflicting item by LUID
561 TSyncItemPContainer::iterator pos;
562 for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
563 if (strcmp((*pos)->getRemoteID(),syncitemP->getRemoteID())==0) {
564 // same LUID exists in data from server
565 PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,(
566 "TStdLogicDS::getConflictingItemByRemoteID, found RemoteID='%s', LocalID='%s', syncop=%s",
567 syncitemP->getRemoteID(),
568 syncitemP->getLocalID(),
569 SyncOpNames[syncitemP->getSyncOp()]
571 return (*pos); // return pointer to item in question
574 PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,("TStdLogicDS::getConflictingItemByRemoteID, no conflicting item"));
576 } // TStdLogicDS::getConflictingItemByRemoteID
579 // called to check if content-matching item from server exists for slow sync
580 TSyncItem *TStdLogicDS::getMatchingItem(TSyncItem *syncitemP, TEqualityMode aEqMode)
582 // search for content matching item
583 TSyncItemPContainer::iterator pos;
584 for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
585 DEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_EXOTIC,(
586 "comparing (this) local item localID='%s' with incoming (other) item remoteID='%s'",
587 (*pos)->getLocalID(),
588 syncitemP->getRemoteID()
590 if ((*pos)->compareWith(
591 *syncitemP,aEqMode,this
593 ,PDEBUGTEST(DBG_DATA+DBG_MATCH+DBG_EXOTIC) // only show comparison if exotic AND match is enabled
596 // items match in content
597 // - check if item is not already matched
598 if ((*pos)->getSyncOp()!=sop_wants_add && (*pos)->getSyncOp()!=sop_reference_only) {
599 // item has already been matched before, so don't match it again
600 DEBUGPRINTFX(DBG_DATA,(
601 "TStdLogicDS::getMatchingItem, match but already used -> skip it: remoteID='%s' = localID='%s'",
602 syncitemP->getRemoteID(),
607 // item has not been matched yet (wannabe add or reference-only), return it now
608 PDEBUGPRINTFX(DBG_DATA+DBG_MATCH+DBG_HOT,(
609 "TStdLogicDS::getMatchingItem, found remoteID='%s' is equal in content with localID='%s'",
610 syncitemP->getRemoteID(),
613 return (*pos); // return pointer to item in question
617 PDEBUGPRINTFX(DBG_DATA+DBG_MATCH,("TStdLogicDS::getMatchingItem, no matching item"));
619 } // TStdLogicDS::getMatchingItem
622 // - called to prevent item to be sent to client in subsequent generateSyncCommands()
623 // item in question should be an item that was returned by getConflictingItemByRemoteID() or getMatchingItem()
624 void TStdLogicDS::dontSendItemAsServer(TSyncItem *syncitemP)
626 PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,("Preventing localID='%s' to be sent to client",syncitemP->getLocalID()));
627 syncitemP->setSyncOp(sop_none); // anyway, set to none
628 // delete from list as we don't need it any more
629 TSyncItemPContainer::iterator pos;
630 for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
631 if (*pos == syncitemP) {
633 PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Item with localID='%s' will NOT be sent to client (usually due to slowsync match)",syncitemP->getLocalID()));
634 delete *pos; // delete item itself
635 fItems.erase(pos); // remove from list
639 } // TStdLogicDS::dontSendItemAsServer
642 // - called when a item in the sync set changes its localID (due to local DB internals)
643 // Datastore must make sure that eventually cached items get updated
644 // - NOTE: derivates must take care of updating map entries as well!
645 void TStdLogicDS::dsLocalIdHasChanged(const char *aOldID, const char *aNewID)
647 PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS::dsLocalIdHasChanged"));
648 // update in loaded list of items
649 TSyncItemPContainer::iterator pos;
650 for (pos=fItems.begin(); pos!=fItems.end(); ++pos) {
651 if (strcmp((*pos)->getLocalID(),aOldID)==0) {
652 // found item, change it's local ID now
653 (*pos)->setLocalID(aNewID);
654 // make sure internal dependencies get updated
655 (*pos)->updateLocalIDDependencies();
660 // let base class do what is needed to update the item itself
661 inherited::dsLocalIdHasChanged(aOldID, aNewID);
662 } // TStdLogicDS::dsLocalIdHasChanged
666 // - called to have additional item sent to remote
667 void TStdLogicDS::SendItemAsServer(TSyncItem *aSyncitemP)
669 // add to list of changes
670 fItems.push_back(aSyncitemP);
671 } // TStdLogicDS::SendItemAsServer
674 // - end map operation (derived class might want to rollback)
675 bool TStdLogicDS::MapFinishAsServer(
676 bool aDoCommit, // if not set, entire map operation must be undone
677 TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
680 // unsuccessful Map will cause rollback of entire datastore transaction
682 // bad, abort session
683 engAbortDataStoreSync(510,true); // data store failed, local problem
686 } // TStdLogicDS::MapFinishAsServer
689 // - called for SyncML 1.1 if remote wants number of changes.
690 // Must return -1 if no NOC value can be returned
691 // NOTE: we implement it here only for server, as it is not really needed
692 // for clients normally - if it is needed, client's agent must provide
693 // it as CustDBDatastore has no own list it can use to count in client case.
694 sInt32 TStdLogicDS::getNumberOfChanges(void)
696 // for server, number of changes is the number of items in the item list
697 // minus those that are for reference only (in a slow sync resume)
698 return fItems.size()-fNumRefOnlyItems;
699 } // TStdLogicDS::getNumberOfChanges
702 /// @brief called to have all non-yet-generated sync commands as "to-be-resumed"
703 void TStdLogicDS::logicMarkOnlyUngeneratedForResume(void)
705 // we do not maintain the map/bookmark list at this level, so
706 // derived class (ODBC, BinFile etc.) must make sure that their list is
707 // clean (no marks from previous sessions) before calling this inherited version
708 implMarkOnlyUngeneratedForResume();
709 // Now add those that we have already received from the implementation
710 TSyncItemPContainer::iterator pos;
711 for (pos = fItems.begin(); pos != fItems.end(); ++pos) {
712 // let datastore mark these unprocessed
713 TSyncItem *syncitemP = (*pos);
714 // mark it for resume by ID
715 logicMarkItemForResume(syncitemP->getLocalID(),syncitemP->getRemoteID(),true); // these are unsent
717 } // TStdLogicDS::logicMarkOnlyUngeneratedForResume
721 // - called to let server generate sync commands for client
722 // Returns true if now finished (or aborted) for this datastore
723 // also sets fState to dss_syncdone when finished
724 bool TStdLogicDS::logicGenerateSyncCommandsAsServer(
725 TSmlCommandPContainer &aNextMessageCommands,
726 TSmlCommand * &aInterruptedCommandP,
727 const char *aLocalIDPrefix
733 // send as many as possible from list of local modifications
734 // sop_want_replace can only be sent if state is already dss_syncfinish
735 TSyncItemPContainer::iterator pos;
736 pos = fItems.begin(); // first item
737 TSyncItemType *itemtypeP = getRemoteReceiveType();
738 POINTERTEST(itemtypeP,("TStdLogicDS::logicGenerateSyncCommandsAsServer: fRemoteReceiveFromLocalTypeP undefined"));
740 if (fMaxItemCount> 0) {
741 PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
742 "Info: Max number of items to be sent in this session is limited to %ld (already sent by now=%ld)",
749 !isAborted() && // not aborted
750 (getDSConfig()->fMaxItemsPerMessage==0 || itemcount<getDSConfig()->fMaxItemsPerMessage==0) && // max item count per message not reached or not active
751 !fSessionP->outgoingMessageFull() && // message not full
752 aNextMessageCommands.size()==0 // no commands already queued for next message
754 // get item to process
755 if (pos == fItems.end()) {
760 TSyncItem *syncitemP = (*pos);
761 // get sync op to perform
762 TSyncOperation syncop=syncitemP->getSyncOp();
763 // check if we can send the item now (for replaces, we need ALWAYS to wait until client has finished sending)
764 // Note: usually sync engine will not start generating before dssta_serverseenclientmods anyway, but...
765 if (syncop==sop_wants_replace && !testState(dssta_serverseenclientmods)) {
766 // cannot be sent now, take next
770 // check if we should ignore this item
771 ignoreitem = syncop==sop_reference_only; // ignore anyway if reference only
772 // further check if not already ignored
774 // - check if adding is still allowed
775 if (fRemoteAddingStopped && (syncop==sop_wants_add || syncop==sop_add)) {
776 // adding to remote has been stopped, discard add items
777 PDEBUGPRINTFX(DBG_DATA,(
778 "Suppressed add for item localID='%s' (fRemoteAddingStopped)",
779 syncitemP->getLocalID()
783 #ifdef SYNCML_TAF_SUPPORT
784 // check other reasons to prevent further adds
785 if (syncop==sop_wants_add || syncop==sop_add) {
786 // - check if max number of items has already been reached
787 if (fMaxItemCount!=0 && fItemsSent>=fMaxItemCount) {
788 PDEBUGPRINTFX(DBG_DATA,(
789 "Suppressed add for item localID='%s' (max item count=%ld reached)",
790 syncitemP->getLocalID(),
795 // - check if item passes eventual TAF
796 /// %%% (do not filter replaces, as these would not get reported again in the next session)
797 /// @todo: the above is no longer true as we can now have them re-sent in next session,
798 /// so this must be changed later!!!
800 syncitemP->testFilter(fTargetAddressFilter.c_str()) &&
801 syncitemP->testFilter(fIntTargetAddressFilter.c_str())
803 PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
804 "Item localID='%s' does not pass INCLUSIVE filter (TAF) -> Suppressed adding",
805 syncitemP->getLocalID()
812 // Now discard if ignored
814 // remove item from list
815 TSyncItemPContainer::iterator temp_pos = pos++; // make copy and set iterator to next
816 fItems.erase(temp_pos); // now entry can be deleted (N.M. Josuttis, pg204)
817 // delete item itself
822 // add prefixes to ID
823 if (syncitemP->hasLocalID()) {
824 // make sure GUID (plus prefixes) is not exceeding allowed size
825 adjustLocalIDforSize(syncitemP->fLocalID,getRemoteDatastore()->getMaxGUIDSize(),aLocalIDPrefix ? strlen(aLocalIDPrefix) : 0);
826 // add local ID prefix, if any
827 if (aLocalIDPrefix && *aLocalIDPrefix)
828 syncitemP->fLocalID.insert(0,aLocalIDPrefix);
830 // create sync op command (may return NULL in case command cannot be created, e.g. for MaxObjSize limitations)
831 TSyncOpCommand *syncopcmdP = newSyncOpCommand(syncitemP,itemtypeP);
832 // erase item from list
834 pos = fItems.erase(pos);
836 // - Note that when command is split, issuePtr returns true, but we still may NOT generate new commands
837 // as the message is already full now. That's why the while contains a check for message full and aNextMessageCommands size
838 // (was not the case before 2.1.0.2, which could cause that the first chunk of a subsequent command
839 // would be sent before the third..nth chunk of the previous command).
840 TSmlCommand *cmdP = syncopcmdP;
842 // eventually, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions)
844 if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) {
845 alldone=false; // issue failed (no room in message), not finished so far
849 fItemsSent++; // overall counter for statistics
850 itemcount++; // per message counter
851 // send event (but no check for abort)
852 OBJ_PROGRESS_EVENT(fSessionP->getSyncAppBase(),pev_itemsent,getDSConfig(),fItemsSent,getNumberOfChanges(),0);
854 }; // while not aborted and not message full
855 // we are not done until all aNextMessageCommands are also out
856 // Note: this must be specially checked because we now have SyncML 1.1 chunked commands.
857 // Those issue() fine, but leave a next chunk in the aNextMessageCommands queue.
858 if (alldone && aNextMessageCommands.size()>0) {
861 // finished when we have done all
862 return (alldone || isAborted());
863 } // TStdLogicDS::logicGenerateSyncCommandsAsServer
866 // called for servers when receiving map from client
867 localstatus TStdLogicDS::logicProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
869 // simply call implementation
870 return implProcessMap(aRemoteID, aLocalID);
871 } // TStdLogicDS::logicProcessMap
880 // called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is ready for being accessed.
881 localstatus TStdLogicDS::startDataAccessForClient(void)
883 DEBUGPRINTFX(DBG_HOT,("TStdLogicDS::startDataAccessForClient"));
886 fEoC=false; // not all changes seen yet
888 #ifdef SCRIPT_SUPPORT
889 // - call DB init script, which might add extra filters depending on options and remoterule
890 TScriptContext::execute(
891 fDataStoreScriptContextP,
892 getDSConfig()->fDBInitScript,
893 &DBFuncTable, // context's function table
894 this // datastore pointer needed for context
897 // - init post fetch filtering, sets fFilteringNeededForAll and fFilteringNeeded correctly
898 initPostFetchFiltering();
899 // - prepare for read
900 localstatus sta=implStartDataRead();
902 } // TStdLogicDS::startDataAccessForClient
905 /// @brief called to have all non-yet-generated sync commands as "to-be-resumed"
906 void TStdLogicDS::logicMarkOnlyUngeneratedForResume(void)
908 // we do not maintain the map/bookmark list at this level, so
909 // derived class (ODBC, BinFile etc.) must make sure that their list is
910 // clean (no marks from previous sessions) before calling this inherited version
911 implMarkOnlyUngeneratedForResume();
912 // in client case, fItems does not contain ungenerated/unprocessed items
913 // so we don't have anything more do here for now
914 } // TStdLogicDS::logicMarkOnlyUngeneratedForResume
918 // called to generate sync sub-commands as client for remote server
919 // @return true if now finished for this datastore
920 bool TStdLogicDS::logicGenerateSyncCommandsAsClient(
921 TSmlCommandPContainer &aNextMessageCommands,
922 TSmlCommand * &aInterruptedCommandP,
923 const char *aLocalIDPrefix
926 localstatus sta = LOCERR_OK;
928 // send as many changed items as possible
929 TSyncItemType *itemtypeP = getRemoteReceiveType();
930 POINTERTEST(itemtypeP,("TStdLogicDS::logicGenerateSyncCommandsAsClient: fRemoteReceiveFromLocalTypeP undefined"));
931 while (!fEoC && !isAborted() && !fSessionP->outgoingMessageFull() && aNextMessageCommands.size()==0) {
932 // get next item from DB
933 TSyncItem *syncitemP = NULL;
934 #ifdef OBJECT_FILTERING
935 bool changed=!fFilteringNeededForAll; // set if we need all records for later filtering
937 bool changed=true; // without filters, always let DB check if modified
939 sta = implGetItem(fEoC,changed,syncitemP);
940 if (sta!=LOCERR_OK) {
942 implEndDataRead(); // terminate reading (error does not matter)
943 engAbortDataStoreSync(sta, true); // local problem
944 return false; // not complete
946 // read successful, test for EoC (end of changes)
948 break; // reading done
949 // get sync op to perform
950 TSyncOperation syncop=syncitemP->getSyncOp();
951 #ifdef OBJECT_FILTERING
953 // - call this anyway (makes sure item is made conformant to remoteAccept filter, even if
954 // fFilteringNeeded is not set)
955 if (syncop!=sop_delete && syncop!=sop_soft_delete && syncop!=sop_archive_delete) {
956 bool passes=postFetchFiltering(syncitemP);
957 if (fFilteringNeeded) {
959 // item does not pass (current) filter: don't send it.
960 // Note that we DO NOT DELETE items falling out of the sync set by filtering,
961 // as for that we'd need to be able to differentiate adds from replaces.
962 // The use case for client-side filtering is also normally not the "moving-subset-window"
963 // case as for server side filtering, but more static exclusion of certain types of
964 // local entries (e.g. to prevent private stuff going to the server).
965 // - we don't need that sync item
973 // add local ID prefix, if any
974 if (aLocalIDPrefix && *aLocalIDPrefix && syncitemP->hasLocalID())
975 syncitemP->fLocalID.insert(0,aLocalIDPrefix);
976 // create sync op command
977 TSyncOpCommand *syncopcmdP = newSyncOpCommand(syncitemP,itemtypeP);
978 #ifdef CLIENT_USES_SERVER_DB
979 // save item, we need it later for post-processing and Map simulation
980 fItems.push_back(syncitemP);
982 // delete item, not used any more
986 // - Note that when command is split, issuePtr returns true, but we still may NOT generate new commands
987 // as the message is already full now. That's why the while contains a check for message full and aNextMessageCommands size
988 // (was not the case before 2.1.0.2, which could cause that the first chunk of a subsequent command
989 // would be sent before the third..nth chunk of the previous command).
990 TSmlCommand *cmdP = syncopcmdP;
992 // eventually, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions)
994 if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) {
995 alldone=false; // issue failed (no room in message), not finished so far
1000 // send event and check for abort
1001 #ifdef PROGRESS_EVENTS
1002 if (!fSessionP->getSyncAppBase()->NotifyProgressEvent(pev_itemsent,getDSConfig(),fItemsSent,getNumberOfChanges())) {
1003 implEndDataRead(); // terminate reading
1004 fSessionP->AbortSession(500,true,LOCERR_USERABORT);
1005 return false; // error
1007 // check for "soft" suspension
1008 if (!fSessionP->getSyncAppBase()->NotifyProgressEvent(pev_suspendcheck)) {
1009 fSessionP->SuspendSession(LOCERR_USERSUSPEND);
1013 }; // while not aborted
1014 // we are not done until all aNextMessageCommands are also out
1015 // Note: this must be specially checked because we now have SyncML 1.1 chunked commands.
1016 // Those issue() fine, but leave a next chunk in the aNextMessageCommands queue.
1017 if (alldone && aNextMessageCommands.size()>0) {
1020 // done if we are now ready for sync or if aborted
1021 return ((alldone && fEoC) || isAborted());
1022 } // TStdLogicDS::logicGenerateSyncCommandsAsClient
1025 #endif // client case
1028 // called to process incoming item operation
1029 // Method takes ownership of syncitemP in all cases
1030 bool TStdLogicDS::logicProcessRemoteItem(
1031 TSyncItem *syncitemP,
1032 TStatusCommand &aStatusCommand,
1033 bool &aVisibleInSyncset, // on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
1034 string *aGUID // GUID is stored here if not NULL
1036 localstatus sta=LOCERR_OK;
1037 bool irregular=false;
1038 bool shouldbevisible=aVisibleInSyncset;
1042 TP_SWITCH(li,fSessionP->fTPInfo,TP_database);
1044 // assume item will stay visible in the syncset after processing
1045 aVisibleInSyncset=true;
1046 // start writing if not already started
1047 sta=startDataWrite();
1048 if (sta!=LOCERR_OK) {
1049 aStatusCommand.setStatusCode(sta);
1053 DEBUGPRINTFX(DBG_DATA,(
1054 "TStdLogicDS::logicProcessRemoteItem starting, SyncOp=%s, RemoteID='%s', LocalID='%s'",
1055 SyncOpNames[syncitemP->getSyncOp()],
1056 syncitemP->getRemoteID(),
1057 syncitemP->getLocalID()
1059 // now perform action
1060 if (syncitemP->getSyncOp()==sop_replace || syncitemP->getSyncOp()==sop_wants_replace) {
1061 // check if we should read before writing
1062 TMultiFieldItem *mfiP;
1063 GET_CASTED_PTR(mfiP,TMultiFieldItem,syncitemP,"");
1064 bool replacewritesallfields = dsReplaceWritesAllDBFields();
1065 bool mightcontaincutoff=false;
1066 // - see if we would pass sync set filter as is
1067 #ifdef OBJECT_FILTERING
1068 aVisibleInSyncset = mfiP->testFilter(fSyncSetFilter.c_str());
1070 !getDSConfig()->fInvisibleFilter.empty() && // has an invisible filter
1071 mfiP->testFilter(getDSConfig()->fInvisibleFilter.c_str()); // and passes it -> invisible
1072 bool visibilityok = (aVisibleInSyncset && !invisible) == shouldbevisible; // check if visibility is correct
1073 if (visibilityok && !replacewritesallfields && aVisibleInSyncset) // avoid expensive check if we have to read anyway
1075 mightcontaincutoff = mfiP->getItemType()->mayContainCutOffData(mfiP->getTargetItemType());
1076 // - if not, we must read item from the DB first and then
1079 #ifdef OBJECT_FILTERING
1082 replacewritesallfields ||
1083 mightcontaincutoff ||
1084 fIgnoreUpdate // if we may not update items, only add them, then we must check first if item exists in DB
1086 // the item we are replacing might contain cut-off data or
1087 // needs otherwise to be modified based on current contents
1088 // we should therefore read item from DB first
1089 // - create new empty TMultiFieldItem
1090 TMultiFieldItem *refitemP =
1091 (TMultiFieldItem *) newItemForRemote(ity_multifield);
1092 #ifdef SYSYNC_CLIENT
1093 // Client: retrieve by local ID
1094 refitemP->clearRemoteID(); // not known
1095 refitemP->setLocalID(syncitemP->getLocalID()); // make sure we retrieve by local ID
1097 // Server: retrieve by remote ID
1098 refitemP->clearLocalID(); // make sure we retrieve by remote ID
1099 refitemP->setRemoteID(syncitemP->getRemoteID()); // make sure we retrieve by remote ID
1101 refitemP->setSyncOp(sop_replace);
1102 #ifdef OBJECT_FILTERING
1103 PDEBUGPRINTFX(DBG_DATA,(
1104 "TStdLogicDS: Need read-modify-write (cause: %s%s%s%s) -> retrieve original item from DB",
1105 !visibilityok ? "visibility_not_ok " : "",
1106 replacewritesallfields ? "replace_writes_all_fields " : "",
1107 mightcontaincutoff ? "might_contain_cutoff_data " : "",
1108 fIgnoreUpdate ? "ignoreUpdate " : ""
1111 PDEBUGPRINTFX(DBG_DATA,(
1112 "TStdLogicDS: Need read-modify-write (cause: %s%s%s) -> retrieve original item from DB",
1113 replacewritesallfields ? "replace_writes_all_fields " : "",
1114 mightcontaincutoff ? "might_contain_cutoff_data " : "",
1115 fIgnoreUpdate ? "ignoreUpdate " : ""
1118 if (implRetrieveItemByID(*refitemP,aStatusCommand)) {
1120 if (PDEBUGTEST(DBG_DATA)) {
1121 PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: Retrieved item"));
1122 if (PDEBUGTEST(DBG_DATA+DBG_DETAILS)) refitemP->debugShowItem(); // show item retrieved
1125 if (fIgnoreUpdate) {
1126 // updates may not be executed at all, and be simply ignored
1127 aStatusCommand.setStatusCode(200); // fake ok.
1128 PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("TStdLogicDS: fIgnoreUpdate set, update command not executed but answered with status 200"));
1133 // we got the item to be replaced
1135 // - only modify available fields
1136 // - perform cutoff prevention
1137 // - do not just copy assigned fields (but all those that are available)
1138 // - IF replace can write individual fields, then transfer unassigned status
1139 // (also for non-availables!) to avoid that datastore needs to write back
1140 // values that are already there.
1141 refitemP->replaceDataFrom(*syncitemP,true,true,false,!replacewritesallfields);
1143 if (PDEBUGTEST(DBG_DATA)) {
1144 PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: Item updated with contents from remote"));
1145 if (PDEBUGTEST(DBG_DATA+DBG_EXOTIC)) refitemP->debugShowItem(); // show item retrieved
1148 #ifdef OBJECT_FILTERING
1149 // - make sure item will pass sync set filter NOW
1150 makePassSyncSetFilter(refitemP);
1151 // - make sure item will be visible NOW
1152 makeVisible(refitemP);
1154 if (PDEBUGTEST(DBG_DATA)) {
1155 PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: Made visible and pass sync set filter"));
1156 if (PDEBUGTEST(DBG_DATA+DBG_EXOTIC)) refitemP->debugShowItem(); // show item retrieved
1160 // - get rid of original
1162 // - use new item for further processing
1166 // failed retrieving item: switch to add
1167 sta = aStatusCommand.getStatusCode();
1168 if (sta==404 || sta==410) {
1169 // this is an irregularity for a client (but it's perfectly
1170 // normal case for server, as client may use replace for adds+replaces)
1171 #ifdef SYSYNC_CLIENT
1172 if (!syncitemP->hasRemoteID()) {
1173 // - we cannot handle this properly, we have no remoteID, so report error to server
1174 PDEBUGPRINTFX(DBG_ERROR,("TStdLogicDS: Item not found, but cannot switch to add because no RemoteID is known, Status=%hd",aStatusCommand.getStatusCode()));
1180 // prevent implicit add -> return sta as is
1183 // - switch to add if remote sent remoteID along so we can properly map
1184 syncitemP->setSyncOp(sop_add);
1185 PDEBUGPRINTFX(DBG_DATA,("TStdLogicDS: RetrieveItem: not found (Status=%hd) --> adding instead",sta));
1186 #ifndef SYSYNC_CLIENT
1187 // - make sure we delete the old item from the map table (as we *know* the item is gone - should it reappear under a different localID, it'll be re-added)
1188 implProcessMap(syncitemP->getRemoteID(),NULL);
1190 // we can handle it, as we know the remoteID
1191 #ifdef SYSYNC_CLIENT
1192 irregular=true; // irregular only for client
1194 sta=LOCERR_OK; // ok for further processing, anyway
1199 // this is a fatal error, report it
1200 PDEBUGPRINTFX(DBG_ERROR,("TStdLogicDS: RetrieveItem failed, Status=%hd",sta));
1202 // get rid of reference item
1207 if (sta==LOCERR_OK) {
1208 // make sure that added items will pass sync set filters. If we can't make them pass
1209 // for DS 1.2 exclusive filters we must generate a delete so we must remember the itempassed status)
1210 #ifdef OBJECT_FILTERING
1211 if (syncitemP->getSyncOp()==sop_add || syncitemP->getSyncOp()==sop_wants_add) {
1212 // - make sure new item will pass sync set filter when re-read from DB
1213 aVisibleInSyncset=makePassSyncSetFilter(syncitemP);
1214 // - also make sure new item has correct visibility status
1215 if (shouldbevisible)
1216 makeVisible(syncitemP); // make visible
1218 aVisibleInSyncset = !makeInvisible(syncitemP); // make invisible
1221 // Now let derived class process the item
1222 if (!implProcessItem(syncitemP,aStatusCommand))
1223 sta = aStatusCommand.getStatusCode(); // not successful, get error status code
1225 // perform special case handling
1226 if (sta!=LOCERR_OK) {
1227 // irregular, special case handling
1228 switch (syncitemP->getSyncOp()) {
1229 case sop_wants_add : // to make sure
1232 // 418: item already exists, this is kind of a conflict
1233 // (should not happen normally, but can happen if aborted session was not
1234 // completely rolled back by server, so treat it like
1235 // "conflict resolved by client data winning")
1236 PDEBUGPRINTFX(DBG_DATA,("to-be-added item already exists, and incomplete rollbacks in server possible -> trying replace (=conflict resolved by client winning)"));
1237 // - switch to replace
1238 syncitemP->setSyncOp(sop_replace);
1241 if (implProcessItem(syncitemP,aStatusCommand)) {
1242 aStatusCommand.setStatusCode(208); // client has won
1245 sta = aStatusCommand.getStatusCode();
1250 if (sta==404 || sta==410) {
1251 // this is an irregularity for a client (but it's perfectly
1252 // normal case for server, as client may use replace for adds+replaces)
1253 #ifdef SYSYNC_CLIENT
1254 if (!syncitemP->hasRemoteID()) {
1255 // - we cannot handle this properly, we have no remoteID, so report error to server
1256 PDEBUGPRINTFX(DBG_ERROR,("to-be-replaced item not found, but cannot switch to add because no RemoteID is known, Status=%hd",sta));
1262 // prevent implicit add -> return status as is
1265 // - switch to add if remote sent remoteID along so we can properly map
1266 syncitemP->setSyncOp(sop_add);
1267 #ifndef SYSYNC_CLIENT
1268 // - make sure we delete the old item from the map table (as we *know* the item is gone - should it reappear under a different localID, it'll be re-added)
1269 implProcessMap(syncitemP->getRemoteID(),NULL);
1271 // we can handle it, as we know the remoteID
1272 #ifdef SYSYNC_CLIENT
1275 PDEBUGPRINTFX(DBG_DATA,("to-be-replaced item not found (Status=%hd) --> adding instead",sta));
1276 // - process again (note that we are re-using the status command that might
1277 // already have a text item with an OS errir if something failed before)
1278 sta=LOCERR_OK; // forget previous status
1279 if (!implProcessItem(syncitemP,aStatusCommand))
1280 sta=aStatusCommand.getStatusCode(); // not successful, get error status code
1286 case sop_archive_delete :
1287 case sop_soft_delete :
1288 if (sta==404 || sta==410) {
1289 if (fSessionP->getSessionConfig()->fDeletingGoneOK) {
1290 // 404/410: item not found, could be because previous aborted session has
1291 // already committed deletion of that item -> behave as if delete was ok
1292 PDEBUGPRINTFX(DBG_DATA,("to-be-deleted item was not found, but do NOT report %hd",sta));
1293 aStatusCommand.setStatusCode(200);
1295 sta = LOCERR_OK; // this is ok, item is deleted already
1300 SYSYNC_THROW(TSyncException("Unknown sync op in TStdLogicDS::logicProcessRemoteItem"));
1304 if (sta==LOCERR_OK) {
1305 PDEBUGPRINTFX(DBG_DATA,(
1306 "- Operation %s performed (%sregular), Remote ID=%s Local ID=%s, status=%hd",
1307 SyncOpNames[syncitemP->getSyncOp()],
1308 irregular ? "ir" : "",
1309 syncitemP->getRemoteID(),
1310 syncitemP->getLocalID(),
1311 aStatusCommand.getStatusCode()
1313 // return GUID if string ptr was passed
1315 (*aGUID)=syncitemP->getLocalID();
1319 PDEBUGPRINTFX(DBG_ERROR,(
1320 "- Operation %s failed with SyncML status=%hd",
1321 SyncOpNames[syncitemP->getSyncOp()],
1325 } // if startDataWrite ok
1326 // anyway, we are done with this item, delete it now
1328 TP_START(fSessionP->fTPInfo,li);
1329 // done, return regular/irregular status
1330 return (sta==LOCERR_OK) && !irregular;
1334 if (syncitemP) delete syncitemP;
1335 TP_START(fSessionP->fTPInfo,li);
1339 #ifndef __BORLANDC__
1340 return false; // BCPPB: unreachable code
1342 } // TStdLogicDS::logicProcessRemoteItem
1346 // Abort datastore sync
1347 void TStdLogicDS::dsAbortDatastoreSync(TSyError aReason, bool aLocalProblem)
1350 inherited::dsAbortDatastoreSync(aReason, aLocalProblem);
1351 } // TStdLogicDS::dsAbortDatastoreSync
1354 // inform logic of coming state change
1355 localstatus TStdLogicDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
1357 localstatus sta=LOCERR_OK;
1359 if (aNewState==dssta_dataaccessstarted) {
1360 // start data access
1361 #ifdef SYSYNC_CLIENT
1362 sta = startDataAccessForClient();
1364 sta = startDataAccessForServer();
1367 #ifdef SYSYNC_CLIENT
1368 if (aNewState==dssta_syncgendone) {
1369 // when client has done sync gen, start writing
1370 sta = startDataWrite();
1373 if (aNewState==dssta_completed && !isAborted()) {
1374 // finish writing data now anyway
1376 // we must save anchors at the moment we shift from any state to dssta_completed
1377 PDEBUGPRINTFX(DBG_ADMIN,("TStdLogicDS: successfully completed, save anchors now"));
1378 // update our level's state
1379 fPreviousSyncTime = fCurrentSyncTime;
1380 // let implementation update their state and save it
1381 sta=implSaveEndOfSession(true);
1382 if (sta!=LOCERR_OK) {
1383 PDEBUGPRINTFX(DBG_ERROR,("TStdLogicDS: Could not save session completed status, err=%hd",sta));
1386 if (aNewState==dssta_idle && aOldState>=dssta_syncsetready) {
1387 // again: make sure data is written anyway (if already done this is a NOP)
1391 if (sta!=LOCERR_OK) return sta;
1392 // let inherited do its stuff as well
1393 return inherited::dsBeforeStateChange(aOldState,aNewState);
1394 } // TStdLogicDS::dsBeforeStateChange
1397 // inform logic of happened state change
1398 localstatus TStdLogicDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState)
1400 localstatus sta=LOCERR_OK;
1402 if (aNewState==dssta_dataaccessdone) {
1403 // finish writing data now
1407 if (sta!=LOCERR_OK) return sta;
1408 // let inherited do its stuff as well
1409 return inherited::dsAfterStateChange(aOldState,aNewState);
1410 } // TStdLogicDS::dsAfterStateChange
1413 /** @deprecated obsolete, replaced by stuff in dsBeforeStateChange()
1414 // called at very end of sync session, when all map commands are done, too
1415 // Note: is also called before deleting a datastore (so aborted sessions
1416 // can do cleanup and/or statistics display as well)
1417 void TStdLogicDS::endOfSync(bool aRegular)
1419 // save new sync anchor now
1420 // NOTE: gets called even if not active
1421 if (fState!=dss_idle) {
1422 PDEBUGPRINTFX(DBG_ADMIN,("TStdLogicDS::endOfSync, %sregular end of sync session",aRegular ? "" : "ir"));
1423 if (!aRegular) fRollback=true; // do not write irregular ends
1424 // datastore was active in sync, end it now
1426 fRollback=!startWrite();
1428 fRollback=!SaveAnchor(fNextRemoteAnchor.c_str());
1433 DEBUGPRINTFX(DBG_ERROR,("************** Datastore error, rolling back transaction"));
1435 // if session is complete, we can't resume it any more, so clear that status now
1437 fResumeAlertCode=0; // no resume
1439 // end writing now, sync is done
1442 // let ancestor do its things
1443 TLocalEngineDS::endOfSync(aRegular);
1444 } // TStdLogicDS::endOfSync
1448 } // namespace sysync
1450 /* end of TStdLogicDS implementation */