- adapted to new SyncClient API: configuration tree handling is now part
of the library - do not reset anchors after failure: allows to proceed with two-way sync git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@196 15ad00c4-1369-45f4-8270-35d70d36bdcd
This commit is contained in:
parent
90e5393a6d
commit
8199a7b38c
34
NEWS
34
NEWS
|
@ -1,20 +1,32 @@
|
|||
SyncEvolution xxx, ????
|
||||
-----------------------
|
||||
* C++ client library revision "funambol30ga": the user
|
||||
agent string can now be modified in the spds/syncml/config.txt,
|
||||
but it is recommended to not set it explicitly. Then
|
||||
SyncEvolution will automatically insert its current
|
||||
version.
|
||||
* patches on top of "funambol30ga" to make the client
|
||||
library compatible with additional servers
|
||||
* fix for library #305795: for tasks the "text/x-todo" type
|
||||
from the configuration was sent to servers instead of the
|
||||
correct "text/calendar" provided by SyncEvolution itself
|
||||
* C++ client library revision "funambol30ga" plus the patches
|
||||
stored in its ".patches" directory:
|
||||
- the user agent string can now be modified in the
|
||||
spds/syncml/config.txt, but it is recommended to not set
|
||||
it explicitly. Then SyncEvolution will automatically insert
|
||||
its current version.
|
||||
- now compatible with additional servers (fixed some SyncML
|
||||
protocol issues, added support for sending device
|
||||
information)
|
||||
- revised API of the client library
|
||||
- #305795: for tasks the "text/x-todo" type from the configuration
|
||||
was sent to servers instead of the correct "text/calendar"
|
||||
provided by SyncEvolution itself
|
||||
* updated default syncml/config.txt: firstTimeSyncMode has never
|
||||
been implemented in the library, removed its documentation
|
||||
* changed vCard 2.1 parser to make it compatible with servers
|
||||
* changed vCard parser to make it compatible with servers
|
||||
which send a verbatim semicolon as part of properties where
|
||||
the semicolon has no special meaning
|
||||
* If minor errors occur like not being able to insert an
|
||||
item at the client or server side, then it is reported in the
|
||||
log and output, but the next synchronization will be a normal
|
||||
synchronization, not a forced slow one as in previous versions.
|
||||
The old approach ensured that the problem was noticed and fixed,
|
||||
but required user assistance. With the new approach synchronization
|
||||
continues to work, although without fixing the root cause of
|
||||
the problem.
|
||||
|
||||
|
||||
|
||||
SyncEvolution 0.3, 2006-06-27
|
||||
|
|
23
README
23
README
|
@ -123,17 +123,18 @@ encountered and print a summary of how the databases were modified.
|
|||
This is done with the "synccompare" utility script described
|
||||
in the "Exchanging Data" section.
|
||||
|
||||
In case of an error the synchronization run is aborted prematurely and
|
||||
SyncEvolution will return a non-zero value. Recovery from failed
|
||||
synchronization is done by forcing a full synchronization during the
|
||||
next run, i.e. by sending all items and letting the SyncML server
|
||||
compare against the ones it already knows. If the error is detected
|
||||
by SyncEvolution itself, then it attempts to cause the full
|
||||
synchronization only for the failed database, but if the SyncML
|
||||
client library itself runs into a problem it might cause a full
|
||||
synchronization of all databases even if only one of them failed.
|
||||
Therefore it might be less risky to invoke SyncEvolution for each
|
||||
data source separately.
|
||||
In case of a severe error the synchronization run is aborted
|
||||
prematurely and SyncEvolution will return a non-zero value. Recovery
|
||||
from failed synchronization is done by forcing a full synchronization
|
||||
during the next run, i.e. by sending all items and letting the SyncML
|
||||
server compare against the ones it already knows. This is avoided
|
||||
whenever possible because matching items during a slow synchronization
|
||||
can lead to duplicate entries.
|
||||
|
||||
Due to a limitation in the client library implementation it might
|
||||
force a slow synchronization of all databases even if only one of them
|
||||
failed. Therefore it might be less risky to invoke SyncEvolution for
|
||||
each data source separately.
|
||||
|
||||
After a successful synchronization the server's configuration file is
|
||||
updated so that the next run can be done incrementally. If the
|
||||
|
|
|
@ -38,17 +38,20 @@ using namespace std;
|
|||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
EvolutionSyncClient::EvolutionSyncClient(const string &server, const set<string> &sources) :
|
||||
EvolutionSyncClient::EvolutionSyncClient(const string &server, SyncMode syncMode,
|
||||
bool doLogging, const set<string> &sources) :
|
||||
m_server(server),
|
||||
m_sources(sources),
|
||||
m_configPath(string("evolution/") + server)
|
||||
m_syncMode(syncMode),
|
||||
m_doLogging(doLogging),
|
||||
m_configPath(string("evolution/") + server),
|
||||
m_sourceList(NULL)
|
||||
{
|
||||
setDMConfig(m_configPath.c_str());
|
||||
}
|
||||
|
||||
EvolutionSyncClient::~EvolutionSyncClient()
|
||||
{
|
||||
Sync4jClient::dispose();
|
||||
}
|
||||
|
||||
/// remove all files in the given directory and the directory itself
|
||||
|
@ -368,48 +371,83 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void EvolutionSyncClient::sync(SyncMode syncMode, bool doLogging)
|
||||
int EvolutionSyncClient::sync()
|
||||
{
|
||||
SourceList sources(m_server, doLogging);
|
||||
// this will trigger the prepareSync(), createSyncSource(), beginSource() callbacks
|
||||
int res = SyncClient::sync();
|
||||
|
||||
DMTree config(m_configPath.c_str());
|
||||
|
||||
// find server URL (part of change id)
|
||||
string serverPath = m_configPath + "/spds/syncml";
|
||||
auto_ptr<ManagementNode> serverNode(config.getManagementNode(serverPath.c_str()));
|
||||
string url = EvolutionSyncSource::getPropertyValue(*serverNode, "syncURL");
|
||||
|
||||
// redirect logging as soon as possible
|
||||
sources.setLogdir(serverNode->getPropertyValue("logdir"),
|
||||
atoi(serverNode->getPropertyValue("maxlogdirs")));
|
||||
|
||||
// find sources
|
||||
string sourcesPath = m_configPath + "/spds/sources";
|
||||
auto_ptr<ManagementNode> sourcesNode(config.getManagementNode(sourcesPath.c_str()));
|
||||
unsigned int index, numSources = sourcesNode->getChildrenMaxCount();
|
||||
char **sourceNamesPtr = sourcesNode->getChildrenNames();
|
||||
|
||||
// copy source names into format that will be
|
||||
// freed in case of exception
|
||||
vector<string> sourceNames;
|
||||
for ( index = 0; index < numSources; index++ ) {
|
||||
sourceNames.push_back(sourceNamesPtr[index]);
|
||||
delete [] sourceNamesPtr[index];
|
||||
#if 0
|
||||
// Change of policy: even if a sync source failed allow
|
||||
// the next sync to proceed normally. The rationale is
|
||||
// that novice users then do not have to care about deleting
|
||||
// "bad" items because the next two-way sync will not stumble
|
||||
// over them.
|
||||
//
|
||||
// Force slow sync in case of failed Evolution source
|
||||
// by overwriting the last sync time stamp;
|
||||
// don't do it if only the general result is a failure
|
||||
// because in that case it is not obvious which source
|
||||
// failed.
|
||||
for ( index = 0; index < m_sourceList->size(); index++ ) {
|
||||
EvolutionSyncSource *source = (*m_sourceList)[index];
|
||||
if (source->hasFailed()) {
|
||||
string sourcePath(sourcesPath + "/" + sourceArray[index]->getName());
|
||||
auto_ptr<ManagementNode> sourceNode(config.getManagementNode(sourcePath.c_str()));
|
||||
sourceNode->setPropertyValue("last", "0");
|
||||
}
|
||||
}
|
||||
delete [] sourceNamesPtr;
|
||||
|
||||
// iterate over sources
|
||||
for ( index = 0; index < numSources; index++ ) {
|
||||
#endif
|
||||
|
||||
if (res) {
|
||||
if (lastErrorCode && lastErrorMsg[0]) {
|
||||
throw runtime_error(lastErrorMsg);
|
||||
}
|
||||
// no error code/description?!
|
||||
throw runtime_error("sync failed without an error description, check log");
|
||||
}
|
||||
|
||||
// all went well: print final report before cleaning up
|
||||
m_sourceList->syncDone(true);
|
||||
delete m_sourceList;
|
||||
m_sourceList = NULL;
|
||||
}
|
||||
|
||||
int EvolutionSyncClient::prepareSync(const AccessConfig &config,
|
||||
ManagementNode &node)
|
||||
{
|
||||
try {
|
||||
// remember for use by sync sources
|
||||
m_url = config.getSyncURL() ? config.getSyncURL() : "";
|
||||
|
||||
// redirect logging as soon as possible
|
||||
m_sourceList = new SourceList(m_server, m_doLogging);
|
||||
m_sourceList->setLogdir(node.getPropertyValue("logdir"),
|
||||
atoi(node.getPropertyValue("maxlogdirs")));
|
||||
} catch(...) {
|
||||
EvolutionSyncSource::handleException();
|
||||
return ERR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
int EvolutionSyncClient::createSyncSource(const char *name,
|
||||
const SyncSourceConfig &config,
|
||||
ManagementNode &node,
|
||||
SyncSource **source)
|
||||
{
|
||||
try {
|
||||
// by default no source for this name
|
||||
*source = NULL;
|
||||
|
||||
// is the source enabled?
|
||||
string sourcePath(sourcesPath + "/" + sourceNames[index]);
|
||||
auto_ptr<ManagementNode> sourceNode(config.getManagementNode(sourcePath.c_str()));
|
||||
string sync = EvolutionSyncSource::getPropertyValue(*sourceNode, "sync");
|
||||
string sync = config.getSync() ? config.getSync() : "";
|
||||
bool enabled = sync != "none";
|
||||
SyncMode overrideMode = SYNC_NONE;
|
||||
|
||||
// override state?
|
||||
if (m_sources.size()) {
|
||||
if (m_sources.find(sourceNames[index]) != m_sources.end()) {
|
||||
if (m_sources.find(name) != m_sources.end()) {
|
||||
if (!enabled) {
|
||||
overrideMode = SYNC_TWO_WAY;
|
||||
enabled = true;
|
||||
|
@ -421,79 +459,54 @@ void EvolutionSyncClient::sync(SyncMode syncMode, bool doLogging)
|
|||
|
||||
if (enabled) {
|
||||
// create it
|
||||
string type = EvolutionSyncSource::getPropertyValue(*sourceNode, "type");
|
||||
string type = config.getType() ? config.getType() : "";
|
||||
EvolutionSyncSource *syncSource =
|
||||
EvolutionSyncSource::createSource(
|
||||
sourceNames[index],
|
||||
string("sync4jevolution:") + url + "/" + EvolutionSyncSource::getPropertyValue(*sourceNode, "name"),
|
||||
EvolutionSyncSource::getPropertyValue(*sourceNode, "evolutionsource"),
|
||||
name,
|
||||
string("sync4jevolution:") + m_url + "/" + name,
|
||||
EvolutionSyncSource::getPropertyValue(node, "evolutionsource"),
|
||||
type
|
||||
);
|
||||
if (!syncSource) {
|
||||
throw runtime_error(sourceNames[index] + ": type " +
|
||||
throw runtime_error(string(name) + ": type " +
|
||||
( type.size() ? string("not configured") :
|
||||
string("'") + type + "' empty or unknown" ));
|
||||
}
|
||||
m_sourceList->push_back(syncSource);
|
||||
|
||||
if (syncMode != SYNC_NONE) {
|
||||
// configure it
|
||||
syncSource->setConfig(config);
|
||||
if (m_syncMode != SYNC_NONE) {
|
||||
// caller overrides mode
|
||||
syncSource->setFixedSyncMode(syncMode);
|
||||
syncSource->setPreferredSyncMode(m_syncMode);
|
||||
} else if (overrideMode != SYNC_NONE) {
|
||||
// disabled source selected via source name
|
||||
syncSource->setFixedSyncMode(overrideMode);
|
||||
syncSource->setPreferredSyncMode(overrideMode);
|
||||
}
|
||||
|
||||
sources.push_back(syncSource);
|
||||
|
||||
// also open it; failing now is still safe
|
||||
syncSource->open();
|
||||
|
||||
// success!
|
||||
*source = syncSource;
|
||||
}
|
||||
} catch(...) {
|
||||
EvolutionSyncSource::handleException();
|
||||
return ERR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
if (!sources.size()) {
|
||||
throw runtime_error("no sources configured");
|
||||
}
|
||||
|
||||
// ready to go: dump initial databases and prepare for final report
|
||||
sources.syncPrepare();
|
||||
|
||||
// build array as sync wants it, then sync
|
||||
// (no exceptions allowed here)
|
||||
SyncSource **sourceArray = new SyncSource *[sources.size() + 1];
|
||||
index = 0;
|
||||
for ( list<EvolutionSyncSource *>::iterator it = sources.begin();
|
||||
it != sources.end();
|
||||
++it ) {
|
||||
sourceArray[index] = *it;
|
||||
++index;
|
||||
}
|
||||
sourceArray[index] = NULL;
|
||||
int res = SyncClient::sync( sourceArray );
|
||||
|
||||
// force slow sync in case of failed Evolution source
|
||||
// by overwriting the last sync time stamp;
|
||||
// don't do it if only the general result is a failure
|
||||
// because in that case it is not obvious which source
|
||||
// failed
|
||||
for ( index = 0; index < sources.size(); index++ ) {
|
||||
if (sourceArray[index] &&
|
||||
((EvolutionSyncSource *)sourceArray[index])->hasFailed()) {
|
||||
string sourcePath(sourcesPath + "/" + sourceArray[index]->getName());
|
||||
auto_ptr<ManagementNode> sourceNode(config.getManagementNode(sourcePath.c_str()));
|
||||
sourceNode->setPropertyValue("last", "0");
|
||||
}
|
||||
}
|
||||
|
||||
delete [] sourceArray;
|
||||
|
||||
if (res) {
|
||||
if (lastErrorCode && lastErrorMsg[0]) {
|
||||
throw runtime_error(lastErrorMsg);
|
||||
}
|
||||
// no error code/description?!
|
||||
throw runtime_error("sync() failed without setting an error description");
|
||||
}
|
||||
|
||||
// all went well: print final report before cleaning up
|
||||
sources.syncDone(true);
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
int EvolutionSyncClient::beginSync()
|
||||
{
|
||||
try {
|
||||
// ready to go: dump initial databases and prepare for final report
|
||||
m_sourceList->syncPrepare();
|
||||
} catch(...) {
|
||||
EvolutionSyncSource::handleException();
|
||||
return ERR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -26,34 +26,45 @@
|
|||
#include <set>
|
||||
using namespace std;
|
||||
|
||||
class SourceList;
|
||||
|
||||
/*
|
||||
* This is the main class inside sync4jevolution which
|
||||
* looks at the configuration, activates all enabled
|
||||
* sources and executes the synchronization.
|
||||
*
|
||||
* Despite the name it is not a Sync4jClient, but rather
|
||||
* uses one.
|
||||
*/
|
||||
class EvolutionSyncClient : private SyncClient {
|
||||
const string m_server;
|
||||
const set<string> m_sources;
|
||||
const SyncMode m_syncMode;
|
||||
const bool m_doLogging;
|
||||
const string m_configPath;
|
||||
string m_url;
|
||||
|
||||
/*
|
||||
* variable shared by sync() and prepareSync():
|
||||
* stores active sync sources and handles reporting
|
||||
*/
|
||||
SourceList *m_sourceList;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param server identifies the server config to be used
|
||||
* @param syncMode setting this overrides the sync mode from the config
|
||||
* @param doLogging write additional log and datatbase files about the sync
|
||||
*/
|
||||
EvolutionSyncClient(const string &server, const set<string> &sources = set<string>());
|
||||
EvolutionSyncClient(const string &server,
|
||||
SyncMode syncMode = SYNC_NONE,
|
||||
bool doLogging = false,
|
||||
const set<string> &sources = set<string>());
|
||||
~EvolutionSyncClient();
|
||||
|
||||
/**
|
||||
* Executes the sync, throws an exception in case of failure.
|
||||
* Handles automatic backups and report generation.
|
||||
*
|
||||
* @param syncMode setting this overrides the sync mode from the config
|
||||
* @param doLogging write additional log and datatbase files about the sync
|
||||
*/
|
||||
void sync(SyncMode syncMode = SYNC_NONE, bool doLogging = false);
|
||||
int sync();
|
||||
|
||||
/**************************************************/
|
||||
/************ override SyncClient interface *******/
|
||||
|
@ -64,6 +75,16 @@ class EvolutionSyncClient : private SyncClient {
|
|||
const char *getManufacturer() const { return "Patrick Ohly"; }
|
||||
const char *getClientType() const { return "workstation"; }
|
||||
int isUTC() const { return true; }
|
||||
|
||||
protected:
|
||||
/* Sync4jClient callbacks */
|
||||
int prepareSync(const AccessConfig &config,
|
||||
ManagementNode &node);
|
||||
int createSyncSource(const char *name,
|
||||
const SyncSourceConfig &config,
|
||||
ManagementNode &node,
|
||||
SyncSource **source);
|
||||
int beginSync();
|
||||
};
|
||||
|
||||
#endif // INCL_EVOLUTIONSYNCCLIENT
|
||||
|
|
|
@ -73,8 +73,7 @@ class EvolutionSyncSource : public SyncSource
|
|||
m_updatedItems( *this, "updated", SYNC_STATE_UPDATED ),
|
||||
m_deletedItems( *this, "deleted", SYNC_STATE_DELETED ),
|
||||
m_hasFailed( false ),
|
||||
m_isModified( false ),
|
||||
m_fixedSyncMode( SYNC_NONE )
|
||||
m_isModified( false )
|
||||
{}
|
||||
virtual ~EvolutionSyncSource() {}
|
||||
|
||||
|
@ -189,18 +188,6 @@ class EvolutionSyncSource : public SyncSource
|
|||
virtual SyncItem* getFirstItemKey() { return getFirstItem(); }
|
||||
virtual SyncItem* getNextItemKey() { return getNextItem(); }
|
||||
|
||||
// if the SyncSource was fixed to a certain mode, then override
|
||||
// the configuration in prepareSync()
|
||||
virtual int prepareSync() {
|
||||
if (m_fixedSyncMode != SYNC_NONE) {
|
||||
setPreferredSyncMode(m_fixedSyncMode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void setFixedSyncMode(SyncMode sourceSyncMode) {
|
||||
m_fixedSyncMode = sourceSyncMode;
|
||||
}
|
||||
|
||||
virtual int beginSync();
|
||||
virtual int endSync();
|
||||
virtual void setItemStatus(const char *key, int status);
|
||||
|
@ -330,9 +317,6 @@ class EvolutionSyncSource : public SyncSource
|
|||
*/
|
||||
bool m_isModified;
|
||||
|
||||
/** if not SYNC_NONE then override preferred sync mode in prepareSync() */
|
||||
SyncMode m_fixedSyncMode;
|
||||
|
||||
private:
|
||||
/**
|
||||
* private wrapper function for add/delete/updateItemThrow()
|
||||
|
|
|
@ -1040,16 +1040,14 @@ template<class T> void TestEvolution<T>::doSync(const string &logfilesuffix, int
|
|||
remove( logfile.c_str() );
|
||||
setLogFile( logfile.c_str(), TRUE );
|
||||
LOG.setLevel(LOG_LEVEL_INFO);
|
||||
{
|
||||
try {
|
||||
set<string> sources;
|
||||
sources.insert(m_source[config]);
|
||||
EvolutionSyncClient client(m_syncConfigs[config], sources);
|
||||
try {
|
||||
client.sync(syncMode);
|
||||
} catch(...) {
|
||||
EvolutionSyncSource::handleException();
|
||||
res = 1;
|
||||
}
|
||||
EvolutionSyncClient client(m_syncConfigs[config], syncMode, false, sources);
|
||||
client.sync();
|
||||
} catch(...) {
|
||||
EvolutionSyncSource::handleException();
|
||||
res = 1;
|
||||
}
|
||||
string oldlogfile = getCurrentTest() + ".log";
|
||||
simplifyFilename(oldlogfile);
|
||||
|
|
|
@ -95,8 +95,8 @@ int main( int argc, char **argv )
|
|||
sources.insert(argv[source]);
|
||||
}
|
||||
|
||||
EvolutionSyncClient client(argv[1], sources);
|
||||
client.sync(SYNC_NONE, true);
|
||||
EvolutionSyncClient client(argv[1], SYNC_NONE, true, sources);
|
||||
client.sync();
|
||||
}
|
||||
return 0;
|
||||
} catch ( const std::exception &ex ) {
|
||||
|
|
Loading…
Reference in New Issue