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
23 #include "syncappbase.h"
24 #include "localengineds.h"
26 #ifdef MULTI_THREAD_DATASTORE
27 #include "platform_thread.h"
30 #if defined(CLIENT_USES_SERVER_DB)
31 #define USES_SERVER_DB 1
32 #define HAS_SERVER_DB 1
35 #define USES_SERVER_DB 1
37 #define HAS_SERVER_DB IS_SERVER
40 using namespace sysync;
44 // container for TSyncItem pointers
45 typedef std::list<sysync::TSyncItem *> TSyncItemPContainer; // contains data items
48 /// @brief standard logic datastore
49 /// - only called directly by TLocalEngineDS via logicXXXX virtuals.
50 /// - to perform actual access to implementation, this class calls its (mostly abstract)
51 /// implXXXX virtuals.
52 class TStdLogicDS: public TLocalEngineDS
54 typedef TLocalEngineDS inherited;
56 bool fWriteStarted; ///< set if write has started
58 bool fEoC; ///< end of changes
59 #if defined(CLIENT_USES_SERVER_DB) && !defined(SYSYNC_SERVER)
60 TSyncItemPContainer fItems; ///< list of data items, used to simulate maps in server DB
64 TSyncItemPContainer fItems; ///< list of data items
65 uInt32 fNumRefOnlyItems;
67 // startSync/threading privates
70 bool fMultiThread; // copied flag from sessionConfig
74 /// @name dsSavedAdmin administrative data (anchors, timestamps, maps) as saved or to-be-saved
75 /// @Note These will be loaded and saved be derived classes
76 /// @Note Some of these will be updated from resp. @ref dsCurrentAdmin members at distinct events (suspend, session end, etc.)
77 /// @Note Some of these will be updated during the session, but in a way that does NOT affect the anchoring of current/last session
80 // - TStdLogicDS is timestamp-based, so we save timestamps of the previous session
81 lineartime_t fPreviousSyncTime; ///< time of previous sync (used to generate local anchor string representing previous sync)
84 /// @name dsCurrentAdmin current session's admin data (anchors, timestamps, maps)
85 /// @Note These will be copied to @ref dsSavedAdmin members ONLY when a session completes successfully/suspends.
86 /// @Note Admin data is NEVER directly saved or loaded from these
87 /// @Note Derivates will update some of these at dssta_adminready with current time/anchor values
90 // - TStdLogicDS is timestamp-based, so we get timestamp to anchor this session
91 lineartime_t fCurrentSyncTime; ///< anchoring timestamp of this, currently running sync.
96 /// internally reset for re-use without re-creation
97 void InternalResetDataStore(void);
101 TLocalDSConfig *aDSConfigP,
102 sysync::TSyncSession *aSessionP,
104 long aCommonSyncCapMask=0);
105 virtual ~TStdLogicDS();
108 /// @name dsProperty property and state querying methods
110 /// check is datastore is completely started.
111 /// @param[in] aWait if set, call will not return until either started state is reached
112 /// or cannot be reached within the maximally allowed request processing time left.
113 virtual bool isStarted(bool aWait);
118 /// @name dsXXXX (usually abstract) virtuals defining the interface to derived datastore classes (implementation, api)
119 /// These are usually designed such that they should always call inherited::dsXXX to let the entire chain
120 /// of ancestors see the calls
123 /// reset datastore to a re-usable, like new-created state.
124 virtual void dsResetDataStore(void) { InternalResetDataStore(); inherited::dsResetDataStore(); };
125 /// abort datastore (no reset yet, everything is just frozen as it is)
126 virtual void dsAbortDatastoreSync(TSyError aStatusCode, bool aLocalProblem);
127 /// inform logic of coming state change
128 virtual localstatus dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
129 /// inform logic of happened state change
130 virtual localstatus dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState);
132 // - called when a item in the sync set changes its localID (due to local DB internals)
133 // Datastore must make sure that possibly cached items get updated
134 virtual void dsLocalIdHasChanged(const char *aOldID, const char *aNewID);
138 /// @name logicXXXX methods defining the interface to TLocalEngineDS.
139 /// Only these will be called by TLocalEnginDS
140 /// @Note some of these are virtuals ONLY for being derived by superdatastore, NEVER by locic or other derivates
141 /// We use the SUPERDS_VIRTUAL macro for these, which is empty in case we don't have superdatastores, then
142 /// these can be non-virtual.
145 /// called to make admin data ready
146 /// - might be called several times (auth retries at beginning of session)
147 virtual localstatus logicMakeAdminReady(cAppCharP aDataStoreURI, cAppCharP aRemoteDBID);
148 /// called to have all non-yet-generated sync commands as "to-be-resumed"
149 virtual void logicMarkOnlyUngeneratedForResume(void);
150 /// called to mark an already generated (but probably not sent or not yet statused) item
151 /// as "to-be-resumed", by localID or remoteID (latter only in server case).
152 /// @note This must be repeatable without side effects, as server must mark/save suspend state
153 /// after every request (and not just at end of session)
154 virtual void logicMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent);
155 /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
156 /// error status conditions, by localID or remoteID (latter only in server case).
157 virtual void logicMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID);
158 /// save status information required to possibly perform a resume (as passed to datastore with
159 /// markOnlyUngeneratedForResume(), markItemForResume() and markItemForResend())
160 /// (or, in case the session is really complete, make sure that no resume state is left)
161 /// @note Must also save tempGUIDs (for server) and pending/unconfirmed maps (for client)
162 virtual localstatus logicSaveResumeMarks(void);
164 /// called to process incoming item operation
165 /// @note Method must take ownership of syncitemP in all cases
166 virtual bool logicProcessRemoteItem(
167 TSyncItem *syncitemP,
168 TStatusCommand &aStatusCommand,
169 bool &aVisibleInSyncset, ///< on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
170 string *aGUID=NULL ///< GUID is stored here if not NULL
172 /// called to read a specified item from the server DB (not restricted to set of conflicting items)
173 virtual bool logicRetrieveItemByID(
174 TSyncItem &aSyncItem, ///< item to be filled with data from server. Local or Remote ID must already be set
175 TStatusCommand &aStatusCommand ///< status, must be set on error or non-200-status
182 /// @name implXXXX methods defining the interface to TStdLogicDS.
183 /// Only these will be called by TLocalEngineDS
184 /// @Note some of these are virtuals ONLY for being derived by superdatastore, NEVER by locic or other derivates
185 /// We use the SUPERDS_VIRTUAL macro for these, which is empty in case we don't have superdatastores, then
186 /// these can be non-virtual.
189 /// save status information required to possibly perform a resume (as passed to datastore with
190 /// markOnlyUngeneratedForResume() and markItemForResume())
191 /// (or, in case the session is really complete, make sure that no resume state is left)
192 /// @note Must also save tempGUIDs (for server) and pending/unconfirmed maps (for client)
193 virtual localstatus implSaveResumeMarks(void) = 0;
194 // - called to have all non-yet-generated sync commands as "to-be-resumed"
195 virtual void implMarkOnlyUngeneratedForResume(void) = 0;
196 /// called to mark an already generated (but probably not sent or not yet statused) item
197 /// as "to-be-resumed", by localID or remoteID (latter only in server case).
198 /// @note This must be repeatable without side effects, as server must mark/save suspend state
199 /// after every request (and not just at end of session)
200 virtual void implMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent) = 0;
201 /// called to mark an already sent item as "to-be-resent", e.g. due to temporary
202 /// error status conditions, by localID or remoteID (latter only in server case).
203 virtual void implMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID) = 0;
204 /// sync login (into this database)
205 /// @note might be called several times (auth retries at beginning of session)
206 /// @note must update the following state variables
207 /// - in TLocalEngineDS: fLastRemoteAnchor, fLastLocalAnchor, fResumeAlertCode, fFirstTimeSync
208 /// - for client: fPendingAddMaps
209 /// - for server: fTempGUIDMap
210 /// - in TStdLogicDS: fPreviousSyncTime, fCurrentSyncTime
211 /// - in derived classes: whatever else belongs to dsSavedAdmin and dsCurrentAdmin state
212 virtual localstatus implMakeAdminReady(
213 const char *aDeviceID, ///< @param[in] remote device URI (device ID)
214 const char *aDatabaseID, ///< @param[in] database ID
215 const char *aRemoteDBID ///< @param[in] database ID of remote device
218 /// @note: fSlowSync and fRefreshOnly must be valid before calling this method
219 virtual localstatus implStartDataRead() = 0;
221 virtual localstatus implGetItem(
222 bool &aEof, ///< set if no more items
223 bool &aChanged, ///< if set on entry, only changed items will be reported, otherwise all will be returned and aChanged denotes if items has changed or not
224 TSyncItem* &aSyncItemP ///< will receive the item
227 virtual localstatus implEndDataRead(void) = 0;
229 virtual localstatus implStartDataWrite(void) = 0;
230 /// Returns true when DB can track syncop changes (i.e. having the DB report
231 /// items as added again when stdlogic filters have decided they fell out of the syncset,
232 /// and has announced this to the DB using implReviewReadItem().
233 virtual bool implTracksSyncopChanges(void) { return false; }; // derived DB class needs to confirm true if
234 /// review reported entry (allows post-processing such as map deleting)
235 /// MUST be called after implStartDataWrite, before any actual writing,
236 /// for each item obtained in implGetItem
237 virtual localstatus implReviewReadItem(
238 TSyncItem &aItem ///< the item
241 /// called to set maps.
242 /// @note aLocalID or aRemoteID can be NULL - which signifies deletion of a map entry
243 /// @note that this might be needed for clients accessing a server-style database as well
244 virtual localstatus implProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID) = 0;
246 /// called to read a specified item from the server DB (not restricted to set of conflicting items)
247 virtual bool implRetrieveItemByID(
248 TSyncItem &aSyncItem, ///< item to be filled with data from server. Local or Remote ID must already be set
249 TStatusCommand &aStatusCommand ///< status, must be set on error or non-200-status
251 /// process item (according to operation: add/delete/replace/map)
252 virtual bool implProcessItem(
253 TSyncItem *aItemP, ///< the item
254 TStatusCommand &aStatusCommand
256 /// save end of session state
257 virtual localstatus implSaveEndOfSession(bool aUpdateAnchors) = 0;
258 /// end write sequence
259 virtual bool implEndDataWrite(void) = 0;
265 /// internal, private helper methods
268 /// called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is ready for being accessed.
269 virtual localstatus startDataAccessForClient(void);
272 /// called by dsBeforeStateChange to dssta_dataaccessstarted to make sure datastore is ready for being accessed.
273 virtual localstatus startDataAccessForServer(void);
281 // - called to check if conflicting replace or delete command from server exists
282 virtual TSyncItem *getConflictingItemByRemoteID(TSyncItem *syncitemP);
283 // - called to check if content-matching item from server exists
284 virtual TSyncItem *getMatchingItem(TSyncItem *syncitemP, TEqualityMode aEqMode);
285 // - called to prevent item to be sent to client in subsequent logicGenerateSyncCommandsAsServer()
286 // item in question should be an item that was returned by getConflictingItemByRemoteID() or getMatchingItem()
287 virtual void dontSendItemAsServer(TSyncItem *syncitemP);
288 // - called to have additional item sent to remote (DB takes ownership of item)
289 virtual void SendItemAsServer(TSyncItem *aSyncitemP);
290 // - end map operation (rollback if not aDoCommit)
291 virtual bool MapFinishAsServer(
292 bool aDoCommit, // if not set, entire map operation must be undone
293 TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
295 /// called to generate sync sub-commands as client for remote server
296 /// @return true if now finished for this datastore
297 virtual bool logicGenerateSyncCommandsAsServer(
298 TSmlCommandPContainer &aNextMessageCommands,
299 TSmlCommand * &aInterruptedCommandP,
300 const char *aLocalIDPrefix
302 /// called for servers when receiving map from client
303 /// @note aLocalID or aRemoteID can be NULL - which signifies deletion of a map entry
304 virtual localstatus logicProcessMap(cAppCharP aLocalID, cAppCharP aRemoteID);
305 #endif // SYSYNC_SERVER
308 /// called to generate sync sub-commands as server for remote client
309 /// @return true if now finished for this datastore
310 virtual bool logicGenerateSyncCommandsAsClient(
311 TSmlCommandPContainer &aNextMessageCommands,
312 TSmlCommand * &aInterruptedCommandP,
313 const char *aLocalIDPrefix
315 #endif // SYSYNC_CLIENT
317 // - determine if this is a first time sync situation
318 virtual bool isFirstTimeSync(void) { return fFirstTimeSync; };
321 // - called for SyncML 1.1 if remote wants number of changes.
322 // Must return -1 no NOC value can be returned
323 // NOTE: we implement it here only for server, as it is not really needed
324 // for clients normally - if it is needed, client's agent must provide
325 // it as CustDBDatastore has no own list it can use to count in client case.
326 virtual sInt32 getNumberOfChanges(void);
329 // Simple custom DB access interface methods
330 // - returns true if database implementation can only update all fields of a record at once
331 virtual bool dsReplaceWritesAllDBFields(void) { return false; } // we assume DB is smart enough
332 #ifdef OBJECT_FILTERING
333 // - returns true if DB implementation can filter during database fetch
334 // (otherwise, fetched items must be filtered after being read from DB)
335 virtual bool dsFilteredFetchesFromDB(bool aFilterChanged=false) { return false; } // assume unfiltered data from DB
339 /// internal stdlogic: start writing if not already started
340 localstatus startDataWrite(void);
341 /// internal stdlogic: end writing if not already ended
342 localstatus endDataWrite(void);
344 // - must be called before starting a thread. If returns false, starting a thread now
345 // is not allowed and must be postponed.
346 virtual bool startingThread(void) { return true; };
347 // - must be called when a thread's activity has ended
348 // BUT THE CALL MUST BE FROM THE ENDING THREAD, not the main thread!
349 virtual void endingThread(void) {};
350 // - should be called before doing DB accesses that might be locked (e.g. because another thread is using the DB resources)
351 virtual bool dbAccessLocked(void) { return false; };
352 // - Actual start sync actions in DB. If server supports threaded init, this will
353 // be called in a sub-thread's context
354 localstatus performStartSync(void);
355 #ifdef MULTI_THREAD_DATASTORE
356 TStatusCommand fStartSyncStatus; // a thread-private status command to store status ocurring during threaded startSync()
359 // - can be called to check if performStartSync() should be terminated
360 bool shouldExitStartSync(void);
361 #ifdef MULTI_THREAD_DATASTORE
362 bool threadedStartSync(void);
363 TThreadObject fStartSyncThread; // the wrapper object for the startSync thread
368 } // namespace sysync
370 #endif // TStdLogicDS_H