akonadi
resourcebase.cpp
00001 /* 00002 Copyright (c) 2006 Till Adam <adam@kde.org> 00003 Copyright (c) 2007 Volker Krause <vkrause@kde.org> 00004 00005 This library is free software; you can redistribute it and/or modify it 00006 under the terms of the GNU Library General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or (at your 00008 option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, but WITHOUT 00011 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00012 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00013 License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to the 00017 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00018 02110-1301, USA. 00019 */ 00020 00021 #include "resourcebase.h" 00022 #include "agentbase_p.h" 00023 00024 #include "resourceadaptor.h" 00025 #include "collectiondeletejob.h" 00026 #include "collectionsync_p.h" 00027 #include "dbusconnectionpool.h" 00028 #include "itemsync.h" 00029 #include "kdepimlibs-version.h" 00030 #include "resourcescheduler_p.h" 00031 #include "tracerinterface.h" 00032 #include "xdgbasedirs_p.h" 00033 00034 #include "changerecorder.h" 00035 #include "collectionfetchjob.h" 00036 #include "collectionfetchscope.h" 00037 #include "collectionmodifyjob.h" 00038 #include "invalidatecachejob_p.h" 00039 #include "itemfetchjob.h" 00040 #include "itemfetchscope.h" 00041 #include "itemmodifyjob.h" 00042 #include "itemmodifyjob_p.h" 00043 #include "session.h" 00044 #include "resourceselectjob_p.h" 00045 #include "monitor_p.h" 00046 #include "servermanager_p.h" 00047 00048 #include <kaboutdata.h> 00049 #include <kcmdlineargs.h> 00050 #include <kdebug.h> 00051 #include <klocale.h> 00052 00053 #include <QtCore/QDebug> 00054 #include <QtCore/QDir> 00055 #include <QtCore/QHash> 00056 #include <QtCore/QSettings> 00057 #include <QtCore/QTimer> 00058 #include <QtGui/QApplication> 00059 #include <QtDBus/QtDBus> 00060 00061 using namespace Akonadi; 00062 00063 class Akonadi::ResourceBasePrivate : public AgentBasePrivate 00064 { 00065 Q_OBJECT 00066 Q_CLASSINFO( "D-Bus Interface", "org.kde.dfaure" ) 00067 00068 public: 00069 ResourceBasePrivate( ResourceBase *parent ) 00070 : AgentBasePrivate( parent ), 00071 scheduler( 0 ), 00072 mItemSyncer( 0 ), 00073 mItemSyncFetchScope( 0 ), 00074 mItemTransactionMode( ItemSync::SingleTransaction ), 00075 mCollectionSyncer( 0 ), 00076 mHierarchicalRid( false ), 00077 mUnemittedProgress( 0 ), 00078 mAutomaticProgressReporting( true ) 00079 { 00080 Internal::setClientType( Internal::Resource ); 00081 mStatusMessage = defaultReadyMessage(); 00082 mProgressEmissionCompressor.setInterval( 1000 ); 00083 mProgressEmissionCompressor.setSingleShot( true ); 00084 } 00085 00086 ~ResourceBasePrivate() 00087 { 00088 delete mItemSyncFetchScope; 00089 } 00090 00091 Q_DECLARE_PUBLIC( ResourceBase ) 00092 00093 void delayedInit() 00094 { 00095 if ( !DBusConnectionPool::threadConnection().registerService( QLatin1String( "org.freedesktop.Akonadi.Resource." ) + mId ) ) { 00096 QString reason = DBusConnectionPool::threadConnection().lastError().message(); 00097 if ( reason.isEmpty() ) { 00098 reason = QString::fromLatin1( "this service is probably running already." ); 00099 } 00100 kError() << "Unable to register service at D-Bus: " << reason; 00101 00102 if ( QThread::currentThread() == QCoreApplication::instance()->thread() ) 00103 QCoreApplication::instance()->exit(1); 00104 00105 } else { 00106 AgentBasePrivate::delayedInit(); 00107 } 00108 } 00109 00110 virtual void changeProcessed() 00111 { 00112 mChangeRecorder->changeProcessed(); 00113 if ( !mChangeRecorder->isEmpty() ) 00114 scheduler->scheduleChangeReplay(); 00115 scheduler->taskDone(); 00116 } 00117 00118 void slotAbortRequested(); 00119 00120 void slotDeliveryDone( KJob* job ); 00121 void slotCollectionSyncDone( KJob *job ); 00122 void slotLocalListDone( KJob *job ); 00123 void slotSynchronizeCollection( const Collection &col ); 00124 void slotCollectionListDone( KJob *job ); 00125 void slotSynchronizeCollectionAttributes( const Collection &col ); 00126 void slotCollectionListForAttributesDone( KJob *job ); 00127 void slotCollectionAttributesSyncDone( KJob *job ); 00128 00129 void slotItemSyncDone( KJob *job ); 00130 00131 void slotPercent( KJob* job, unsigned long percent ); 00132 void slotDelayedEmitProgress(); 00133 void slotDeleteResourceCollection(); 00134 void slotDeleteResourceCollectionDone( KJob *job ); 00135 void slotCollectionDeletionDone( KJob *job ); 00136 00137 void slotInvalidateCache( const Akonadi::Collection &collection ); 00138 00139 void slotPrepareItemRetrieval( const Akonadi::Item &item ); 00140 void slotPrepareItemRetrievalResult( KJob* job ); 00141 00142 void changeCommittedResult( KJob* job ); 00143 00144 void slotSessionReconnected() 00145 { 00146 Q_Q( ResourceBase ); 00147 00148 new ResourceSelectJob( q->identifier() ); 00149 } 00150 00151 void createItemSyncInstanceIfMissing() 00152 { 00153 Q_Q( ResourceBase ); 00154 Q_ASSERT_X( scheduler->currentTask().type == ResourceScheduler::SyncCollection, 00155 "createItemSyncInstance", "Calling items retrieval methods although no item retrieval is in progress" ); 00156 if ( !mItemSyncer ) { 00157 mItemSyncer = new ItemSync( q->currentCollection() ); 00158 mItemSyncer->setTransactionMode( mItemTransactionMode ); 00159 if ( mItemSyncFetchScope ) 00160 mItemSyncer->setFetchScope( *mItemSyncFetchScope ); 00161 mItemSyncer->setProperty( "collection", QVariant::fromValue( q->currentCollection() ) ); 00162 connect( mItemSyncer, SIGNAL(percent(KJob*,ulong)), q, SLOT(slotPercent(KJob*,ulong)) ); 00163 connect( mItemSyncer, SIGNAL(result(KJob*)), q, SLOT(slotItemSyncDone(KJob*)) ); 00164 } 00165 Q_ASSERT( mItemSyncer ); 00166 } 00167 00168 public Q_SLOTS: 00169 // Dump the contents of the current ChangeReplay 00170 Q_SCRIPTABLE QString dumpNotificationListToString() const 00171 { 00172 return mChangeRecorder->dumpNotificationListToString(); 00173 } 00174 00175 // Dump the state of the scheduler 00176 Q_SCRIPTABLE QString dumpToString() const 00177 { 00178 return scheduler->dumpToString(); 00179 } 00180 00181 Q_SCRIPTABLE void dump() 00182 { 00183 scheduler->dump(); 00184 } 00185 00186 Q_SCRIPTABLE void clear() 00187 { 00188 scheduler->clear(); 00189 } 00190 00191 protected Q_SLOTS: 00192 // reimplementations from AgentbBasePrivate, containing sanity checks that only apply to resources 00193 // such as making sure that RIDs are present as well as translations of cross-resource moves 00194 // TODO: we could possibly add recovery code for no-RID notifications by re-enquing those to the change recorder 00195 // as the corresponding Add notifications, although that contains a risk of endless fail/retry loops 00196 00197 void itemAdded(const Akonadi::Item& item, const Akonadi::Collection& collection) 00198 { 00199 if ( collection.remoteId().isEmpty() ) { 00200 changeProcessed(); 00201 return; 00202 } 00203 AgentBasePrivate::itemAdded( item, collection ); 00204 } 00205 00206 void itemChanged(const Akonadi::Item& item, const QSet< QByteArray >& partIdentifiers) 00207 { 00208 if ( item.remoteId().isEmpty() ) { 00209 changeProcessed(); 00210 return; 00211 } 00212 AgentBasePrivate::itemChanged( item, partIdentifiers ); 00213 } 00214 00215 // TODO move the move translation code from AgebtBasePrivate here, it's wrong for agents 00216 void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &destination) 00217 { 00218 if ( item.remoteId().isEmpty() || destination.remoteId().isEmpty() || destination == source ) { 00219 changeProcessed(); 00220 return; 00221 } 00222 AgentBasePrivate::itemMoved( item, source, destination ); 00223 } 00224 00225 void itemRemoved(const Akonadi::Item& item) 00226 { 00227 if ( item.remoteId().isEmpty() ) { 00228 changeProcessed(); 00229 return; 00230 } 00231 AgentBasePrivate::itemRemoved( item ); 00232 } 00233 00234 void collectionAdded(const Akonadi::Collection& collection, const Akonadi::Collection& parent) 00235 { 00236 if ( parent.remoteId().isEmpty() ) { 00237 changeProcessed(); 00238 return; 00239 } 00240 AgentBasePrivate::collectionAdded( collection, parent ); 00241 } 00242 00243 void collectionChanged(const Akonadi::Collection& collection) 00244 { 00245 if ( collection.remoteId().isEmpty() ) { 00246 changeProcessed(); 00247 return; 00248 } 00249 AgentBasePrivate::collectionChanged( collection ); 00250 } 00251 00252 void collectionChanged(const Akonadi::Collection& collection, const QSet< QByteArray >& partIdentifiers) 00253 { 00254 if ( collection.remoteId().isEmpty() ) { 00255 changeProcessed(); 00256 return; 00257 } 00258 AgentBasePrivate::collectionChanged( collection, partIdentifiers ); 00259 } 00260 00261 // TODO move the move translation code from AgebtBasePrivate here, it's wrong for agents 00262 void collectionMoved(const Akonadi::Collection& collection, const Akonadi::Collection& source, const Akonadi::Collection& destination) 00263 { 00264 if ( collection.remoteId().isEmpty() || destination.remoteId().isEmpty() || source == destination ) { 00265 changeProcessed(); 00266 return; 00267 } 00268 AgentBasePrivate::collectionMoved( collection, source, destination ); 00269 } 00270 00271 void collectionRemoved(const Akonadi::Collection& collection) 00272 { 00273 if ( collection.remoteId().isEmpty() ) { 00274 changeProcessed(); 00275 return; 00276 } 00277 AgentBasePrivate::collectionRemoved( collection ); 00278 } 00279 00280 public: 00281 // synchronize states 00282 Collection currentCollection; 00283 00284 ResourceScheduler *scheduler; 00285 ItemSync *mItemSyncer; 00286 ItemFetchScope *mItemSyncFetchScope; 00287 ItemSync::TransactionMode mItemTransactionMode; 00288 CollectionSync *mCollectionSyncer; 00289 bool mHierarchicalRid; 00290 QTimer mProgressEmissionCompressor; 00291 int mUnemittedProgress; 00292 QMap<Akonadi::Collection::Id, QVariantMap> mUnemittedAdvancedStatus; 00293 bool mAutomaticProgressReporting; 00294 }; 00295 00296 ResourceBase::ResourceBase( const QString & id ) 00297 : AgentBase( new ResourceBasePrivate( this ), id ) 00298 { 00299 Q_D( ResourceBase ); 00300 00301 new Akonadi__ResourceAdaptor( this ); 00302 00303 d->scheduler = new ResourceScheduler( this ); 00304 00305 d->mChangeRecorder->setChangeRecordingEnabled( true ); 00306 connect( d->mChangeRecorder, SIGNAL(changesAdded()), 00307 d->scheduler, SLOT(scheduleChangeReplay()) ); 00308 00309 d->mChangeRecorder->setResourceMonitored( d->mId.toLatin1() ); 00310 d->mChangeRecorder->fetchCollection( true ); 00311 00312 connect( d->scheduler, SIGNAL(executeFullSync()), 00313 SLOT(retrieveCollections()) ); 00314 connect( d->scheduler, SIGNAL(executeCollectionTreeSync()), 00315 SLOT(retrieveCollections()) ); 00316 connect( d->scheduler, SIGNAL(executeCollectionSync(Akonadi::Collection)), 00317 SLOT(slotSynchronizeCollection(Akonadi::Collection)) ); 00318 connect( d->scheduler, SIGNAL(executeCollectionAttributesSync(Akonadi::Collection)), 00319 SLOT(slotSynchronizeCollectionAttributes(Akonadi::Collection)) ); 00320 connect( d->scheduler, SIGNAL(executeItemFetch(Akonadi::Item,QSet<QByteArray>)), 00321 SLOT(slotPrepareItemRetrieval(Akonadi::Item)) ); 00322 connect( d->scheduler, SIGNAL(executeResourceCollectionDeletion()), 00323 SLOT(slotDeleteResourceCollection()) ); 00324 connect ( d->scheduler, SIGNAL(executeCacheInvalidation(Akonadi::Collection)), 00325 SLOT(slotInvalidateCache(Akonadi::Collection)) ); 00326 connect( d->scheduler, SIGNAL(status(int,QString)), 00327 SIGNAL(status(int,QString)) ); 00328 connect( d->scheduler, SIGNAL(executeChangeReplay()), 00329 d->mChangeRecorder, SLOT(replayNext()) ); 00330 connect( d->scheduler, SIGNAL(fullSyncComplete()), SIGNAL(synchronized()) ); 00331 connect( d->scheduler, SIGNAL(collectionTreeSyncComplete()), SIGNAL(collectionTreeSynchronized()) ); 00332 connect( d->mChangeRecorder, SIGNAL(nothingToReplay()), d->scheduler, SLOT(taskDone()) ); 00333 connect( d->mChangeRecorder, SIGNAL(collectionRemoved(Akonadi::Collection)), 00334 d->scheduler, SLOT(collectionRemoved(Akonadi::Collection)) ); 00335 connect( this, SIGNAL(abortRequested()), this, SLOT(slotAbortRequested()) ); 00336 connect( this, SIGNAL(synchronized()), d->scheduler, SLOT(taskDone()) ); 00337 connect( this, SIGNAL(collectionTreeSynchronized()), d->scheduler, SLOT(taskDone()) ); 00338 connect( this, SIGNAL(agentNameChanged(QString)), 00339 this, SIGNAL(nameChanged(QString)) ); 00340 00341 connect( &d->mProgressEmissionCompressor, SIGNAL(timeout()), 00342 this, SLOT(slotDelayedEmitProgress()) ); 00343 00344 d->scheduler->setOnline( d->mOnline ); 00345 if ( !d->mChangeRecorder->isEmpty() ) 00346 d->scheduler->scheduleChangeReplay(); 00347 00348 DBusConnectionPool::threadConnection().registerObject( QLatin1String( "/Debug" ), d, QDBusConnection::ExportScriptableSlots ); 00349 00350 new ResourceSelectJob( identifier() ); 00351 00352 connect( d->mChangeRecorder->session(), SIGNAL(reconnected()), SLOT(slotSessionReconnected()) ); 00353 } 00354 00355 ResourceBase::~ResourceBase() 00356 { 00357 } 00358 00359 void ResourceBase::synchronize() 00360 { 00361 d_func()->scheduler->scheduleFullSync(); 00362 } 00363 00364 void ResourceBase::setName( const QString &name ) 00365 { 00366 AgentBase::setAgentName( name ); 00367 } 00368 00369 QString ResourceBase::name() const 00370 { 00371 return AgentBase::agentName(); 00372 } 00373 00374 QString ResourceBase::parseArguments( int argc, char **argv ) 00375 { 00376 QString identifier; 00377 if ( argc < 3 ) { 00378 kDebug() << "Not enough arguments passed..."; 00379 exit( 1 ); 00380 } 00381 00382 for ( int i = 1; i < argc - 1; ++i ) { 00383 if ( QLatin1String( argv[ i ] ) == QLatin1String( "--identifier" ) ) 00384 identifier = QLatin1String( argv[ i + 1 ] ); 00385 } 00386 00387 if ( identifier.isEmpty() ) { 00388 kDebug() << "Identifier argument missing"; 00389 exit( 1 ); 00390 } 00391 00392 const QFileInfo fi( QString::fromLocal8Bit( argv[0] ) ); 00393 // strip off full path and possible .exe suffix 00394 const QByteArray catalog = fi.baseName().toLatin1(); 00395 00396 KCmdLineArgs::init( argc, argv, identifier.toLatin1(), catalog, 00397 ki18nc( "@title application name", "Akonadi Resource" ), KDEPIMLIBS_VERSION, 00398 ki18nc( "@title application description", "Akonadi Resource" ) ); 00399 00400 KCmdLineOptions options; 00401 options.add( "identifier <argument>", 00402 ki18nc( "@label commandline option", "Resource identifier" ) ); 00403 KCmdLineArgs::addCmdLineOptions( options ); 00404 00405 return identifier; 00406 } 00407 00408 int ResourceBase::init( ResourceBase *r ) 00409 { 00410 QApplication::setQuitOnLastWindowClosed( false ); 00411 KGlobal::locale()->insertCatalog( QLatin1String( "libakonadi" ) ); 00412 int rv = kapp->exec(); 00413 delete r; 00414 return rv; 00415 } 00416 00417 void ResourceBasePrivate::slotAbortRequested() 00418 { 00419 Q_Q( ResourceBase ); 00420 00421 scheduler->cancelQueues(); 00422 QMetaObject::invokeMethod( q, "abortActivity" ); 00423 } 00424 00425 void ResourceBase::itemRetrieved( const Item &item ) 00426 { 00427 Q_D( ResourceBase ); 00428 Q_ASSERT( d->scheduler->currentTask().type == ResourceScheduler::FetchItem ); 00429 if ( !item.isValid() ) { 00430 d->scheduler->currentTask().sendDBusReplies( false ); 00431 d->scheduler->taskDone(); 00432 return; 00433 } 00434 00435 Item i( item ); 00436 QSet<QByteArray> requestedParts = d->scheduler->currentTask().itemParts; 00437 foreach ( const QByteArray &part, requestedParts ) { 00438 if ( !item.loadedPayloadParts().contains( part ) ) { 00439 kWarning() << "Item does not provide part" << part; 00440 } 00441 } 00442 00443 ItemModifyJob *job = new ItemModifyJob( i ); 00444 // FIXME: remove once the item with which we call retrieveItem() has a revision number 00445 job->disableRevisionCheck(); 00446 connect( job, SIGNAL(result(KJob*)), SLOT(slotDeliveryDone(KJob*)) ); 00447 } 00448 00449 void ResourceBasePrivate::slotDeliveryDone(KJob * job) 00450 { 00451 Q_Q( ResourceBase ); 00452 Q_ASSERT( scheduler->currentTask().type == ResourceScheduler::FetchItem ); 00453 if ( job->error() ) { 00454 emit q->error( QLatin1String( "Error while creating item: " ) + job->errorString() ); 00455 } 00456 scheduler->currentTask().sendDBusReplies( !job->error() ); 00457 scheduler->taskDone(); 00458 } 00459 00460 void ResourceBase::collectionAttributesRetrieved( const Collection &collection ) 00461 { 00462 Q_D( ResourceBase ); 00463 Q_ASSERT( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes ); 00464 if ( !collection.isValid() ) { 00465 emit attributesSynchronized( d->scheduler->currentTask().collection.id() ); 00466 d->scheduler->taskDone(); 00467 return; 00468 } 00469 00470 CollectionModifyJob *job = new CollectionModifyJob( collection ); 00471 connect( job, SIGNAL(result(KJob*)), SLOT(slotCollectionAttributesSyncDone(KJob*)) ); 00472 } 00473 00474 void ResourceBasePrivate::slotCollectionAttributesSyncDone(KJob * job) 00475 { 00476 Q_Q( ResourceBase ); 00477 Q_ASSERT( scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes ); 00478 if ( job->error() ) { 00479 emit q->error( QLatin1String( "Error while updating collection: " ) + job->errorString() ); 00480 } 00481 emit q->attributesSynchronized( scheduler->currentTask().collection.id() ); 00482 scheduler->taskDone(); 00483 } 00484 00485 void ResourceBasePrivate::slotDeleteResourceCollection() 00486 { 00487 Q_Q( ResourceBase ); 00488 00489 CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel ); 00490 job->fetchScope().setResource( q->identifier() ); 00491 connect( job, SIGNAL(result(KJob*)), q, SLOT(slotDeleteResourceCollectionDone(KJob*)) ); 00492 } 00493 00494 void ResourceBasePrivate::slotDeleteResourceCollectionDone( KJob *job ) 00495 { 00496 Q_Q( ResourceBase ); 00497 if ( job->error() ) { 00498 emit q->error( job->errorString() ); 00499 scheduler->taskDone(); 00500 } else { 00501 const CollectionFetchJob *fetchJob = static_cast<const CollectionFetchJob*>( job ); 00502 00503 if ( !fetchJob->collections().isEmpty() ) { 00504 CollectionDeleteJob *job = new CollectionDeleteJob( fetchJob->collections().first() ); 00505 connect( job, SIGNAL(result(KJob*)), q, SLOT(slotCollectionDeletionDone(KJob*)) ); 00506 } else { 00507 // there is no resource collection, so just ignore the request 00508 scheduler->taskDone(); 00509 } 00510 } 00511 } 00512 00513 void ResourceBasePrivate::slotCollectionDeletionDone( KJob *job ) 00514 { 00515 Q_Q( ResourceBase ); 00516 if ( job->error() ) { 00517 emit q->error( job->errorString() ); 00518 } 00519 00520 scheduler->taskDone(); 00521 } 00522 00523 void ResourceBasePrivate::slotInvalidateCache( const Akonadi::Collection &collection ) 00524 { 00525 Q_Q( ResourceBase ); 00526 InvalidateCacheJob *job = new InvalidateCacheJob( collection, q ); 00527 connect( job, SIGNAL(result(KJob*)), scheduler, SLOT(taskDone()) ); 00528 } 00529 00530 void ResourceBase::changeCommitted( const Item& item ) 00531 { 00532 Q_D( ResourceBase ); 00533 ItemModifyJob *job = new ItemModifyJob( item ); 00534 job->d_func()->setClean(); 00535 job->disableRevisionCheck(); // TODO: remove, but where/how do we handle the error? 00536 job->setIgnorePayload( true ); // we only want to reset the dirty flag and update the remote id 00537 d->changeProcessed(); 00538 } 00539 00540 void ResourceBase::changeCommitted( const Collection &collection ) 00541 { 00542 CollectionModifyJob *job = new CollectionModifyJob( collection ); 00543 connect( job, SIGNAL(result(KJob*)), SLOT(changeCommittedResult(KJob*)) ); 00544 } 00545 00546 void ResourceBasePrivate::changeCommittedResult( KJob *job ) 00547 { 00548 Q_Q( ResourceBase ); 00549 if ( job->error() ) 00550 emit q->error( i18nc( "@info", "Updating local collection failed: %1.", job->errorText() ) ); 00551 mChangeRecorder->d_ptr->invalidateCache( static_cast<CollectionModifyJob*>( job )->collection() ); 00552 changeProcessed(); 00553 } 00554 00555 bool ResourceBase::requestItemDelivery( qint64 uid, const QString & remoteId, 00556 const QString &mimeType, const QStringList &_parts ) 00557 { 00558 Q_D( ResourceBase ); 00559 if ( !isOnline() ) { 00560 emit error( i18nc( "@info", "Cannot fetch item in offline mode." ) ); 00561 return false; 00562 } 00563 00564 setDelayedReply( true ); 00565 // FIXME: we need at least the revision number too 00566 Item item( uid ); 00567 item.setMimeType( mimeType ); 00568 item.setRemoteId( remoteId ); 00569 00570 QSet<QByteArray> parts; 00571 Q_FOREACH( const QString &str, _parts ) 00572 parts.insert( str.toLatin1() ); 00573 00574 d->scheduler->scheduleItemFetch( item, parts, message().createReply() ); 00575 00576 return true; 00577 } 00578 00579 void ResourceBase::collectionsRetrieved( const Collection::List & collections ) 00580 { 00581 Q_D( ResourceBase ); 00582 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || 00583 d->scheduler->currentTask().type == ResourceScheduler::SyncAll, 00584 "ResourceBase::collectionsRetrieved()", 00585 "Calling collectionsRetrieved() although no collection retrieval is in progress" ); 00586 if ( !d->mCollectionSyncer ) { 00587 d->mCollectionSyncer = new CollectionSync( identifier() ); 00588 d->mCollectionSyncer->setHierarchicalRemoteIds( d->mHierarchicalRid ); 00589 connect( d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)) ); 00590 connect( d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) ); 00591 } 00592 d->mCollectionSyncer->setRemoteCollections( collections ); 00593 } 00594 00595 void ResourceBase::collectionsRetrievedIncremental( const Collection::List & changedCollections, 00596 const Collection::List & removedCollections ) 00597 { 00598 Q_D( ResourceBase ); 00599 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || 00600 d->scheduler->currentTask().type == ResourceScheduler::SyncAll, 00601 "ResourceBase::collectionsRetrievedIncremental()", 00602 "Calling collectionsRetrievedIncremental() although no collection retrieval is in progress" ); 00603 if ( !d->mCollectionSyncer ) { 00604 d->mCollectionSyncer = new CollectionSync( identifier() ); 00605 d->mCollectionSyncer->setHierarchicalRemoteIds( d->mHierarchicalRid ); 00606 connect( d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)) ); 00607 connect( d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) ); 00608 } 00609 d->mCollectionSyncer->setRemoteCollections( changedCollections, removedCollections ); 00610 } 00611 00612 void ResourceBase::setCollectionStreamingEnabled( bool enable ) 00613 { 00614 Q_D( ResourceBase ); 00615 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || 00616 d->scheduler->currentTask().type == ResourceScheduler::SyncAll, 00617 "ResourceBase::setCollectionStreamingEnabled()", 00618 "Calling setCollectionStreamingEnabled() although no collection retrieval is in progress" ); 00619 if ( !d->mCollectionSyncer ) { 00620 d->mCollectionSyncer = new CollectionSync( identifier() ); 00621 d->mCollectionSyncer->setHierarchicalRemoteIds( d->mHierarchicalRid ); 00622 connect( d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)) ); 00623 connect( d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) ); 00624 } 00625 d->mCollectionSyncer->setStreamingEnabled( enable ); 00626 } 00627 00628 void ResourceBase::collectionsRetrievalDone() 00629 { 00630 Q_D( ResourceBase ); 00631 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree || 00632 d->scheduler->currentTask().type == ResourceScheduler::SyncAll, 00633 "ResourceBase::collectionsRetrievalDone()", 00634 "Calling collectionsRetrievalDone() although no collection retrieval is in progress" ); 00635 // streaming enabled, so finalize the sync 00636 if ( d->mCollectionSyncer ) { 00637 d->mCollectionSyncer->retrievalDone(); 00638 } 00639 // user did the sync himself, we are done now 00640 else { 00641 // FIXME: we need the same special case for SyncAll as in slotCollectionSyncDone here! 00642 d->scheduler->taskDone(); 00643 } 00644 } 00645 00646 void ResourceBasePrivate::slotCollectionSyncDone( KJob * job ) 00647 { 00648 Q_Q( ResourceBase ); 00649 mCollectionSyncer = 0; 00650 if ( job->error() ) { 00651 if ( job->error() != Job::UserCanceled ) 00652 emit q->error( job->errorString() ); 00653 } else { 00654 if ( scheduler->currentTask().type == ResourceScheduler::SyncAll ) { 00655 CollectionFetchJob *list = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive ); 00656 list->setFetchScope( q->changeRecorder()->collectionFetchScope() ); 00657 list->fetchScope().setResource( mId ); 00658 q->connect( list, SIGNAL(result(KJob*)), q, SLOT(slotLocalListDone(KJob*)) ); 00659 return; 00660 } else if ( scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ) { 00661 scheduler->scheduleCollectionTreeSyncCompletion(); 00662 } 00663 } 00664 scheduler->taskDone(); 00665 } 00666 00667 void ResourceBasePrivate::slotLocalListDone( KJob * job ) 00668 { 00669 Q_Q( ResourceBase ); 00670 if ( job->error() ) { 00671 emit q->error( job->errorString() ); 00672 } else { 00673 Collection::List cols = static_cast<CollectionFetchJob*>( job )->collections(); 00674 foreach ( const Collection &col, cols ) { 00675 scheduler->scheduleSync( col ); 00676 } 00677 scheduler->scheduleFullSyncCompletion(); 00678 } 00679 scheduler->taskDone(); 00680 } 00681 00682 void ResourceBasePrivate::slotSynchronizeCollection( const Collection &col ) 00683 { 00684 Q_Q( ResourceBase ); 00685 currentCollection = col; 00686 // check if this collection actually can contain anything 00687 QStringList contentTypes = currentCollection.contentMimeTypes(); 00688 contentTypes.removeAll( Collection::mimeType() ); 00689 if ( !contentTypes.isEmpty() || (col.rights() & (Collection::CanLinkItem)) ) { // HACK to check for virtual collections 00690 if ( mAutomaticProgressReporting ) { 00691 emit q->status( AgentBase::Running, i18nc( "@info:status", "Syncing folder '%1'", currentCollection.name() ) ); 00692 } 00693 q->retrieveItems( currentCollection ); 00694 return; 00695 } 00696 scheduler->taskDone(); 00697 } 00698 00699 void ResourceBasePrivate::slotSynchronizeCollectionAttributes( const Collection &col ) 00700 { 00701 Q_Q( ResourceBase ); 00702 QMetaObject::invokeMethod( q, "retrieveCollectionAttributes", Q_ARG( Akonadi::Collection, col ) ); 00703 } 00704 00705 void ResourceBasePrivate::slotPrepareItemRetrieval( const Akonadi::Item &item ) 00706 { 00707 Q_Q( ResourceBase ); 00708 ItemFetchJob *fetch = new ItemFetchJob( item, this ); 00709 fetch->fetchScope().setAncestorRetrieval( q->changeRecorder()->itemFetchScope().ancestorRetrieval() ); 00710 fetch->fetchScope().setCacheOnly( true ); 00711 00712 // copy list of attributes to fetch 00713 const QSet<QByteArray> attributes = q->changeRecorder()->itemFetchScope().attributes(); 00714 foreach ( const QByteArray &attribute, attributes ) 00715 fetch->fetchScope().fetchAttribute( attribute ); 00716 00717 q->connect( fetch, SIGNAL(result(KJob*)), SLOT(slotPrepareItemRetrievalResult(KJob*)) ); 00718 } 00719 00720 void ResourceBasePrivate::slotPrepareItemRetrievalResult( KJob* job ) 00721 { 00722 Q_Q( ResourceBase ); 00723 Q_ASSERT_X( scheduler->currentTask().type == ResourceScheduler::FetchItem, 00724 "ResourceBasePrivate::slotPrepareItemRetrievalResult()", 00725 "Preparing item retrieval although no item retrieval is in progress" ); 00726 if ( job->error() ) { 00727 q->cancelTask( job->errorText() ); 00728 return; 00729 } 00730 ItemFetchJob *fetch = qobject_cast<ItemFetchJob*>( job ); 00731 if ( fetch->items().count() != 1 ) { 00732 q->cancelTask( i18n( "The requested item no longer exists" ) ); 00733 return; 00734 } 00735 const Item item = fetch->items().first(); 00736 const QSet<QByteArray> parts = scheduler->currentTask().itemParts; 00737 if ( !q->retrieveItem( item, parts ) ) 00738 q->cancelTask(); 00739 } 00740 00741 void ResourceBase::itemsRetrievalDone() 00742 { 00743 Q_D( ResourceBase ); 00744 // streaming enabled, so finalize the sync 00745 if ( d->mItemSyncer ) { 00746 d->mItemSyncer->deliveryDone(); 00747 } 00748 // user did the sync himself, we are done now 00749 else { 00750 d->scheduler->taskDone(); 00751 } 00752 } 00753 00754 void ResourceBase::clearCache() 00755 { 00756 Q_D( ResourceBase ); 00757 d->scheduler->scheduleResourceCollectionDeletion(); 00758 } 00759 00760 void ResourceBase::invalidateCache(const Collection& collection) 00761 { 00762 Q_D( ResourceBase ); 00763 d->scheduler->scheduleCacheInvalidation( collection ); 00764 } 00765 00766 Collection ResourceBase::currentCollection() const 00767 { 00768 Q_D( const ResourceBase ); 00769 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollection , 00770 "ResourceBase::currentCollection()", 00771 "Trying to access current collection although no item retrieval is in progress" ); 00772 return d->currentCollection; 00773 } 00774 00775 Item ResourceBase::currentItem() const 00776 { 00777 Q_D( const ResourceBase ); 00778 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::FetchItem , 00779 "ResourceBase::currentItem()", 00780 "Trying to access current item although no item retrieval is in progress" ); 00781 return d->scheduler->currentTask().item; 00782 } 00783 00784 void ResourceBase::synchronizeCollectionTree() 00785 { 00786 d_func()->scheduler->scheduleCollectionTreeSync(); 00787 } 00788 00789 void ResourceBase::cancelTask() 00790 { 00791 Q_D( ResourceBase ); 00792 switch ( d->scheduler->currentTask().type ) { 00793 case ResourceScheduler::FetchItem: 00794 itemRetrieved( Item() ); // sends the error reply and 00795 break; 00796 case ResourceScheduler::ChangeReplay: 00797 d->changeProcessed(); 00798 break; 00799 case ResourceScheduler::SyncCollectionTree: 00800 case ResourceScheduler::SyncAll: 00801 if ( d->mCollectionSyncer ) 00802 d->mCollectionSyncer->rollback(); 00803 else 00804 d->scheduler->taskDone(); 00805 break; 00806 case ResourceScheduler::SyncCollection: 00807 if ( d->mItemSyncer ) { 00808 d->mItemSyncer->rollback(); 00809 } else { 00810 d->scheduler->taskDone(); 00811 } 00812 break; 00813 default: 00814 d->scheduler->taskDone(); 00815 } 00816 } 00817 00818 void ResourceBase::cancelTask( const QString &msg ) 00819 { 00820 cancelTask(); 00821 00822 emit error( msg ); 00823 } 00824 00825 void ResourceBase::deferTask() 00826 { 00827 Q_D( ResourceBase ); 00828 d->scheduler->deferTask(); 00829 } 00830 00831 void ResourceBase::doSetOnline( bool state ) 00832 { 00833 d_func()->scheduler->setOnline( state ); 00834 } 00835 00836 void ResourceBase::synchronizeCollection( qint64 collectionId ) 00837 { 00838 synchronizeCollection( collectionId, false ); 00839 } 00840 00841 void ResourceBase::synchronizeCollection( qint64 collectionId, bool recursive ) 00842 { 00843 CollectionFetchJob* job = new CollectionFetchJob( Collection( collectionId ), recursive ? CollectionFetchJob::Recursive : CollectionFetchJob::Base ); 00844 job->setFetchScope( changeRecorder()->collectionFetchScope() ); 00845 job->fetchScope().setResource( identifier() ); 00846 job->setProperty( "recursive", recursive ); 00847 connect( job, SIGNAL(result(KJob*)), SLOT(slotCollectionListDone(KJob*)) ); 00848 } 00849 00850 void ResourceBasePrivate::slotCollectionListDone( KJob *job ) 00851 { 00852 if ( !job->error() ) { 00853 Collection::List list = static_cast<CollectionFetchJob*>( job )->collections(); 00854 if ( !list.isEmpty() ) { 00855 if ( job->property( "recursive" ).toBool() ) { 00856 Q_FOREACH ( const Collection &collection, list ) { 00857 scheduler->scheduleSync( collection ); 00858 } 00859 } else { 00860 scheduler->scheduleSync( list.first() ); 00861 } 00862 } 00863 } 00864 // TODO: error handling 00865 } 00866 00867 void ResourceBase::synchronizeCollectionAttributes( qint64 collectionId ) 00868 { 00869 CollectionFetchJob* job = new CollectionFetchJob( Collection( collectionId ), CollectionFetchJob::Base ); 00870 job->setFetchScope( changeRecorder()->collectionFetchScope() ); 00871 job->fetchScope().setResource( identifier() ); 00872 connect( job, SIGNAL(result(KJob*)), SLOT(slotCollectionListForAttributesDone(KJob*)) ); 00873 } 00874 00875 void ResourceBasePrivate::slotCollectionListForAttributesDone( KJob *job ) 00876 { 00877 if ( !job->error() ) { 00878 Collection::List list = static_cast<CollectionFetchJob*>( job )->collections(); 00879 if ( !list.isEmpty() ) { 00880 Collection col = list.first(); 00881 scheduler->scheduleAttributesSync( col ); 00882 } 00883 } 00884 // TODO: error handling 00885 } 00886 00887 void ResourceBase::setTotalItems( int amount ) 00888 { 00889 kDebug() << amount; 00890 Q_D( ResourceBase ); 00891 setItemStreamingEnabled( true ); 00892 if ( d->mItemSyncer ) { 00893 d->mItemSyncer->setTotalItems( amount ); 00894 } 00895 } 00896 00897 void ResourceBase::setItemStreamingEnabled( bool enable ) 00898 { 00899 Q_D( ResourceBase ); 00900 d->createItemSyncInstanceIfMissing(); 00901 if ( d->mItemSyncer ) { 00902 d->mItemSyncer->setStreamingEnabled( enable ); 00903 } 00904 } 00905 00906 void ResourceBase::itemsRetrieved( const Item::List &items ) 00907 { 00908 Q_D( ResourceBase ); 00909 d->createItemSyncInstanceIfMissing(); 00910 if ( d->mItemSyncer ) { 00911 d->mItemSyncer->setFullSyncItems( items ); 00912 } 00913 } 00914 00915 void ResourceBase::itemsRetrievedIncremental( const Item::List &changedItems, 00916 const Item::List &removedItems ) 00917 { 00918 Q_D( ResourceBase ); 00919 d->createItemSyncInstanceIfMissing(); 00920 if ( d->mItemSyncer ) { 00921 d->mItemSyncer->setIncrementalSyncItems( changedItems, removedItems ); 00922 } 00923 } 00924 00925 void ResourceBasePrivate::slotItemSyncDone( KJob *job ) 00926 { 00927 mItemSyncer = 0; 00928 Q_Q( ResourceBase ); 00929 if ( job->error() && job->error() != Job::UserCanceled ) { 00930 emit q->error( job->errorString() ); 00931 } 00932 scheduler->taskDone(); 00933 } 00934 00935 00936 void ResourceBasePrivate::slotDelayedEmitProgress() 00937 { 00938 Q_Q( ResourceBase ); 00939 if ( mAutomaticProgressReporting ) { 00940 emit q->percent( mUnemittedProgress ); 00941 00942 Q_FOREACH( const QVariantMap &statusMap, mUnemittedAdvancedStatus ) { 00943 emit q->advancedStatus( statusMap ); 00944 } 00945 } 00946 mUnemittedProgress = 0; 00947 mUnemittedAdvancedStatus.clear(); 00948 } 00949 00950 void ResourceBasePrivate::slotPercent( KJob *job, unsigned long percent ) 00951 { 00952 mUnemittedProgress = percent; 00953 00954 const Collection collection = job->property( "collection" ).value<Collection>(); 00955 if ( collection.isValid() ) { 00956 QVariantMap statusMap; 00957 statusMap.insert( QLatin1String( "key" ), QString::fromLatin1( "collectionSyncProgress" ) ); 00958 statusMap.insert( QLatin1String( "collectionId" ), collection.id() ); 00959 statusMap.insert( QLatin1String( "percent" ), static_cast<unsigned int>( percent ) ); 00960 00961 mUnemittedAdvancedStatus[collection.id()] = statusMap; 00962 } 00963 // deliver completion right away, intermediate progress at 1s intervals 00964 if ( percent == 100 ) { 00965 mProgressEmissionCompressor.stop(); 00966 slotDelayedEmitProgress(); 00967 } else if ( !mProgressEmissionCompressor.isActive() ) { 00968 mProgressEmissionCompressor.start(); 00969 } 00970 } 00971 00972 void ResourceBase::setHierarchicalRemoteIdentifiersEnabled( bool enable ) 00973 { 00974 Q_D( ResourceBase ); 00975 d->mHierarchicalRid = enable; 00976 } 00977 00978 void ResourceBase::scheduleCustomTask( QObject *receiver, const char *method, const QVariant &argument, SchedulePriority priority ) 00979 { 00980 Q_D( ResourceBase ); 00981 d->scheduler->scheduleCustomTask( receiver, method, argument, priority ); 00982 } 00983 00984 void ResourceBase::taskDone() 00985 { 00986 Q_D( ResourceBase ); 00987 d->scheduler->taskDone(); 00988 } 00989 00990 void ResourceBase::retrieveCollectionAttributes( const Collection &collection ) 00991 { 00992 collectionAttributesRetrieved( collection ); 00993 } 00994 00995 void Akonadi::ResourceBase::abortActivity() 00996 { 00997 00998 } 00999 01000 void ResourceBase::setItemTransactionMode(ItemSync::TransactionMode mode) 01001 { 01002 Q_D( ResourceBase ); 01003 d->mItemTransactionMode = mode; 01004 } 01005 01006 void ResourceBase::setItemSynchronizationFetchScope(const ItemFetchScope& fetchScope) 01007 { 01008 Q_D( ResourceBase ); 01009 if ( !d->mItemSyncFetchScope ) 01010 d->mItemSyncFetchScope = new ItemFetchScope; 01011 *(d->mItemSyncFetchScope) = fetchScope; 01012 } 01013 01014 void ResourceBase::setAutomaticProgressReporting( bool enabled ) 01015 { 01016 Q_D( ResourceBase ); 01017 d->mAutomaticProgressReporting = enabled; 01018 } 01019 01020 QString ResourceBase::dumpNotificationListToString() const 01021 { 01022 Q_D( const ResourceBase ); 01023 return d->dumpNotificationListToString(); 01024 } 01025 01026 QString ResourceBase::dumpSchedulerToString() const 01027 { 01028 Q_D( const ResourceBase ); 01029 return d->dumpToString(); 01030 } 01031 01032 #include "resourcebase.moc" 01033 #include "moc_resourcebase.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:09:24 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:09:24 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.