00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "resourcebase.h"
00022 #include "agentbase_p.h"
00023
00024 #include "resourceadaptor.h"
00025 #include "collectionsync.h"
00026 #include "itemsync.h"
00027 #include "resourcescheduler.h"
00028 #include "tracerinterface.h"
00029 #include "xdgbasedirs_p.h"
00030
00031 #include "changerecorder.h"
00032 #include "collectionfetchjob.h"
00033 #include "collectionmodifyjob.h"
00034 #include "itemfetchjob.h"
00035 #include "itemfetchscope.h"
00036 #include "itemmodifyjob.h"
00037 #include "itemmodifyjob_p.h"
00038 #include "session.h"
00039
00040 #include <kaboutdata.h>
00041 #include <kcmdlineargs.h>
00042 #include <kdebug.h>
00043 #include <klocale.h>
00044
00045 #include <QtCore/QDebug>
00046 #include <QtCore/QDir>
00047 #include <QtCore/QHash>
00048 #include <QtCore/QSettings>
00049 #include <QtCore/QTimer>
00050 #include <QtGui/QApplication>
00051 #include <QtDBus/QtDBus>
00052
00053 using namespace Akonadi;
00054
00055 class Akonadi::ResourceBasePrivate : public AgentBasePrivate
00056 {
00057 public:
00058 ResourceBasePrivate( ResourceBase *parent )
00059 : AgentBasePrivate( parent ),
00060 scheduler( 0 ),
00061 mItemSyncer( 0 )
00062 {
00063 mStatusMessage = defaultReadyMessage();
00064 }
00065
00066 Q_DECLARE_PUBLIC( ResourceBase )
00067
00068 void delayedInit()
00069 {
00070 if ( !QDBusConnection::sessionBus().registerService( QLatin1String( "org.freedesktop.Akonadi.Resource." ) + mId ) )
00071 kFatal() << "Unable to register service at D-Bus: " << QDBusConnection::sessionBus().lastError().message();
00072 AgentBasePrivate::delayedInit();
00073 }
00074
00075 virtual void changeProcessed()
00076 {
00077 mMonitor->changeProcessed();
00078 if ( !mMonitor->isEmpty() )
00079 scheduler->scheduleChangeReplay();
00080 scheduler->taskDone();
00081 }
00082
00083 void slotDeliveryDone( KJob* job );
00084
00085 void slotCollectionSyncDone( KJob *job );
00086 void slotLocalListDone( KJob *job );
00087 void slotSynchronizeCollection( const Collection &col );
00088 void slotCollectionListDone( KJob *job );
00089
00090 void slotItemSyncDone( KJob *job );
00091
00092 void slotPercent( KJob* job, unsigned long percent );
00093
00094 QString mName;
00095
00096
00097 Collection currentCollection;
00098
00099 ResourceScheduler *scheduler;
00100 ItemSync *mItemSyncer;
00101 };
00102
00103 ResourceBase::ResourceBase( const QString & id )
00104 : AgentBase( new ResourceBasePrivate( this ), id )
00105 {
00106 Q_D( ResourceBase );
00107
00108 new ResourceAdaptor( this );
00109
00110 const QString name = d->mSettings->value( QLatin1String( "Resource/Name" ) ).toString();
00111 if ( !name.isEmpty() )
00112 d->mName = name;
00113
00114 d->scheduler = new ResourceScheduler( this );
00115
00116 d->mMonitor->setChangeRecordingEnabled( true );
00117 connect( d->mMonitor, SIGNAL(changesAdded()),
00118 d->scheduler, SLOT(scheduleChangeReplay()) );
00119
00120 d->mMonitor->setResourceMonitored( d->mId.toLatin1() );
00121
00122 connect( d->scheduler, SIGNAL(executeFullSync()),
00123 SLOT(retrieveCollections()) );
00124 connect( d->scheduler, SIGNAL(executeCollectionTreeSync()),
00125 SLOT(retrieveCollections()) );
00126 connect( d->scheduler, SIGNAL(executeCollectionSync(Akonadi::Collection)),
00127 SLOT(slotSynchronizeCollection(Akonadi::Collection)) );
00128 connect( d->scheduler, SIGNAL(executeItemFetch(Akonadi::Item,QSet<QByteArray>)),
00129 SLOT(retrieveItem(Akonadi::Item,QSet<QByteArray>)) );
00130 connect( d->scheduler, SIGNAL(executeChangeReplay()),
00131 d->mMonitor, SLOT(replayNext()) );
00132
00133 d->scheduler->setOnline( d->mOnline );
00134 if ( !d->mMonitor->isEmpty() )
00135 d->scheduler->scheduleChangeReplay();
00136 }
00137
00138 ResourceBase::~ResourceBase()
00139 {
00140 }
00141
00142 void ResourceBase::synchronize()
00143 {
00144 d_func()->scheduler->scheduleFullSync();
00145 }
00146
00147 void ResourceBase::setName( const QString &name )
00148 {
00149 Q_D( ResourceBase );
00150 if ( name == d->mName )
00151 return;
00152
00153
00154 d->mName = name;
00155
00156 if ( d->mName.isEmpty() || d->mName == d->mId )
00157 d->mSettings->remove( QLatin1String( "Resource/Name" ) );
00158 else
00159 d->mSettings->setValue( QLatin1String( "Resource/Name" ), d->mName );
00160
00161 d->mSettings->sync();
00162
00163 emit nameChanged( d->mName );
00164 }
00165
00166 QString ResourceBase::name() const
00167 {
00168 Q_D( const ResourceBase );
00169 if ( d->mName.isEmpty() )
00170 return d->mId;
00171 else
00172 return d->mName;
00173 }
00174
00175 static char* sAppName = 0;
00176
00177 QString ResourceBase::parseArguments( int argc, char **argv )
00178 {
00179 QString identifier;
00180 if ( argc < 3 ) {
00181 kDebug( 5250 ) << "Not enough arguments passed...";
00182 exit( 1 );
00183 }
00184
00185 for ( int i = 1; i < argc - 1; ++i ) {
00186 if ( QLatin1String( argv[ i ] ) == QLatin1String( "--identifier" ) )
00187 identifier = QLatin1String( argv[ i + 1 ] );
00188 }
00189
00190 if ( identifier.isEmpty() ) {
00191 kDebug( 5250 ) << "Identifier argument missing";
00192 exit( 1 );
00193 }
00194
00195 sAppName = qstrdup( identifier.toLatin1().constData() );
00196 KCmdLineArgs::init( argc, argv, sAppName, 0,
00197 ki18nc("@title, application name", "Akonadi Resource"), "0.1",
00198 ki18nc("@title, application description", "Akonadi Resource") );
00199
00200 KCmdLineOptions options;
00201 options.add("identifier <argument>",
00202 ki18nc("@label, commandline option", "Resource identifier"));
00203 KCmdLineArgs::addCmdLineOptions( options );
00204
00205 return identifier;
00206 }
00207
00208 int ResourceBase::init( ResourceBase *r )
00209 {
00210 QApplication::setQuitOnLastWindowClosed( false );
00211 int rv = kapp->exec();
00212 delete r;
00213 delete[] sAppName;
00214 return rv;
00215 }
00216
00217 void ResourceBase::itemRetrieved( const Item &item )
00218 {
00219 Q_D( ResourceBase );
00220 Q_ASSERT( d->scheduler->currentTask().type == ResourceScheduler::FetchItem );
00221 if ( !item.isValid() ) {
00222 QDBusMessage reply( d->scheduler->currentTask().dbusMsg );
00223 reply << false;
00224 QDBusConnection::sessionBus().send( reply );
00225 d->scheduler->taskDone();
00226 return;
00227 }
00228
00229 Item i( item );
00230 QSet<QByteArray> requestedParts = d->scheduler->currentTask().itemParts;
00231 foreach ( const QByteArray &part, requestedParts ) {
00232 if ( !item.loadedPayloadParts().contains( part ) ) {
00233 kWarning( 5250 ) << "Item does not provide part" << part;
00234 }
00235 }
00236
00237 ItemModifyJob *job = new ItemModifyJob( i );
00238
00239 job->disableRevisionCheck();
00240 connect( job, SIGNAL(result(KJob*)), SLOT(slotDeliveryDone(KJob*)) );
00241 }
00242
00243 void ResourceBasePrivate::slotDeliveryDone(KJob * job)
00244 {
00245 Q_Q( ResourceBase );
00246 Q_ASSERT( scheduler->currentTask().type == ResourceScheduler::FetchItem );
00247 QDBusMessage reply( scheduler->currentTask().dbusMsg );
00248 if ( job->error() ) {
00249 emit q->error( QLatin1String( "Error while creating item: " ) + job->errorString() );
00250 reply << false;
00251 } else {
00252 reply << true;
00253 }
00254 QDBusConnection::sessionBus().send( reply );
00255 scheduler->taskDone();
00256 }
00257
00258 void ResourceBase::changeCommitted(const Item& item)
00259 {
00260 Q_D( ResourceBase );
00261 ItemModifyJob *job = new ItemModifyJob( item );
00262 job->d_func()->setClean();
00263 job->disableRevisionCheck();
00264 d->changeProcessed();
00265 }
00266
00267 void ResourceBase::changeCommitted( const Collection &collection )
00268 {
00269 Q_D( ResourceBase );
00270 CollectionModifyJob *job = new CollectionModifyJob( collection );
00271 Q_UNUSED( job );
00272
00273 d->changeProcessed();
00274 }
00275
00276 bool ResourceBase::requestItemDelivery( qint64 uid, const QString & remoteId,
00277 const QString &mimeType, const QStringList &_parts )
00278 {
00279 Q_D( ResourceBase );
00280 if ( !isOnline() ) {
00281 emit error( i18nc( "@info", "Cannot fetch item in offline mode." ) );
00282 return false;
00283 }
00284
00285 setDelayedReply( true );
00286
00287 Item item( uid );
00288 item.setMimeType( mimeType );
00289 item.setRemoteId( remoteId );
00290
00291 QSet<QByteArray> parts;
00292 Q_FOREACH( const QString &str, _parts )
00293 parts.insert( str.toLatin1() );
00294
00295 d->scheduler->scheduleItemFetch( item, parts, message().createReply() );
00296
00297 return true;
00298 }
00299
00300 void ResourceBase::collectionsRetrieved(const Collection::List & collections)
00301 {
00302 Q_D( ResourceBase );
00303 CollectionSync *syncer = new CollectionSync( d->mId );
00304 syncer->setRemoteCollections( collections );
00305 connect( syncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) );
00306 }
00307
00308 void ResourceBase::collectionsRetrievedIncremental(const Collection::List & changedCollections, const Collection::List & removedCollections)
00309 {
00310 Q_D( ResourceBase );
00311 CollectionSync *syncer = new CollectionSync( d->mId );
00312 syncer->setRemoteCollections( changedCollections, removedCollections );
00313 connect( syncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) );
00314 }
00315
00316 void ResourceBasePrivate::slotCollectionSyncDone(KJob * job)
00317 {
00318 Q_Q( ResourceBase );
00319 if ( job->error() ) {
00320 emit q->error( job->errorString() );
00321 } else {
00322 if ( scheduler->currentTask().type == ResourceScheduler::SyncAll ) {
00323 CollectionFetchJob *list = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive );
00324 list->setResource( mId );
00325 q->connect( list, SIGNAL(result(KJob*)), q, SLOT(slotLocalListDone(KJob*)) );
00326 return;
00327 }
00328 }
00329 if ( scheduler->isEmpty() )
00330 emit q->status( AgentBase::Idle );
00331 scheduler->taskDone();
00332 }
00333
00334 void ResourceBasePrivate::slotLocalListDone(KJob * job)
00335 {
00336 Q_Q( ResourceBase );
00337 if ( job->error() ) {
00338 emit q->error( job->errorString() );
00339 } else {
00340 Collection::List cols = static_cast<CollectionFetchJob*>( job )->collections();
00341 foreach ( const Collection &col, cols ) {
00342 scheduler->scheduleSync( col );
00343 }
00344 }
00345 scheduler->taskDone();
00346 }
00347
00348 void ResourceBasePrivate::slotSynchronizeCollection( const Collection &col )
00349 {
00350 Q_Q( ResourceBase );
00351 currentCollection = col;
00352
00353 QStringList contentTypes = currentCollection.contentMimeTypes();
00354 contentTypes.removeAll( Collection::mimeType() );
00355 if ( !contentTypes.isEmpty() ) {
00356 emit q->status( AgentBase::Running, i18nc( "@info:status", "Syncing collection '%1'", currentCollection.name() ) );
00357 q->retrieveItems( currentCollection );
00358 return;
00359 }
00360 scheduler->taskDone();
00361 }
00362
00363 void ResourceBase::itemsRetrievalDone()
00364 {
00365 Q_D( ResourceBase );
00366
00367 if ( d->mItemSyncer ) {
00368 d->mItemSyncer->deliveryDone();
00369 }
00370
00371 else {
00372 if ( d->scheduler->isEmpty() )
00373 emit status( Idle );
00374 d->scheduler->taskDone();
00375 }
00376 }
00377
00378 Collection ResourceBase::currentCollection() const
00379 {
00380 Q_D( const ResourceBase );
00381 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollection ,
00382 "ResourceBase::currentCollection()",
00383 "Trying to access current collection although no item retrieval is in progress" );
00384 return d->currentCollection;
00385 }
00386
00387 Item ResourceBase::currentItem() const
00388 {
00389 Q_D( const ResourceBase );
00390 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::FetchItem ,
00391 "ResourceBase::currentItem()",
00392 "Trying to access current item although no item retrieval is in progress" );
00393 return d->scheduler->currentTask().item;
00394 }
00395
00396 void ResourceBase::synchronizeCollectionTree()
00397 {
00398 d_func()->scheduler->scheduleCollectionTreeSync();
00399 }
00400
00401 void ResourceBase::cancelTask()
00402 {
00403 d_func()->changeProcessed();
00404 }
00405
00406 void ResourceBase::cancelTask( const QString &msg )
00407 {
00408 cancelTask();
00409
00410 emit error( msg );
00411 }
00412
00413 void ResourceBase::doSetOnline( bool state )
00414 {
00415 d_func()->scheduler->setOnline( state );
00416 }
00417
00418 void ResourceBase::synchronizeCollection(qint64 collectionId )
00419 {
00420 CollectionFetchJob* job = new CollectionFetchJob( Collection(collectionId), CollectionFetchJob::Base );
00421 job->setResource( identifier() );
00422 connect( job, SIGNAL(result(KJob*)), SLOT(slotCollectionListDone(KJob*)) );
00423 }
00424
00425 void ResourceBasePrivate::slotCollectionListDone( KJob *job )
00426 {
00427 if ( !job->error() ) {
00428 Collection::List list = static_cast<CollectionFetchJob*>( job )->collections();
00429 if ( !list.isEmpty() ) {
00430 Collection col = list.first();
00431 scheduler->scheduleSync( col );
00432 }
00433 }
00434
00435 }
00436
00437 void ResourceBase::setTotalItems( int amount )
00438 {
00439 kDebug() << amount;
00440 Q_D( ResourceBase );
00441 setItemStreamingEnabled( true );
00442 d->mItemSyncer->setTotalItems( amount );
00443 }
00444
00445 void ResourceBase::setItemStreamingEnabled( bool enable )
00446 {
00447 Q_D( ResourceBase );
00448 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollection,
00449 "ResourceBase::setItemStreamingEnabled()",
00450 "Calling setItemStreamingEnabled() although no item retrieval is in progress" );
00451 if ( !d->mItemSyncer ) {
00452 d->mItemSyncer = new ItemSync( currentCollection() );
00453 connect( d->mItemSyncer, SIGNAL(percent(KJob*,unsigned long)), SLOT(slotPercent(KJob*,unsigned long)) );
00454 connect( d->mItemSyncer, SIGNAL(result(KJob*)), SLOT(slotItemSyncDone(KJob*)) );
00455 }
00456 d->mItemSyncer->setStreamingEnabled( enable );
00457 }
00458
00459 void ResourceBase::itemsRetrieved( const Item::List &items )
00460 {
00461 Q_D( ResourceBase );
00462 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollection,
00463 "ResourceBase::itemsRetrieved()",
00464 "Calling itemsRetrieved() although no item retrieval is in progress" );
00465 if ( !d->mItemSyncer ) {
00466 d->mItemSyncer = new ItemSync( currentCollection() );
00467 connect( d->mItemSyncer, SIGNAL(percent(KJob*,unsigned long)), SLOT(slotPercent(KJob*,unsigned long)) );
00468 connect( d->mItemSyncer, SIGNAL(result(KJob*)), SLOT(slotItemSyncDone(KJob*)) );
00469 }
00470 d->mItemSyncer->setFullSyncItems( items );
00471 }
00472
00473 void ResourceBase::itemsRetrievedIncremental(const Item::List &changedItems, const Item::List &removedItems)
00474 {
00475 Q_D( ResourceBase );
00476 Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollection,
00477 "ResourceBase::itemsRetrievedIncremental()",
00478 "Calling itemsRetrievedIncremental() although no item retrieval is in progress" );
00479 if ( !d->mItemSyncer ) {
00480 d->mItemSyncer = new ItemSync( currentCollection() );
00481 connect( d->mItemSyncer, SIGNAL(percent(KJob*,unsigned long)), SLOT(slotPercent(KJob*,unsigned long)) );
00482 connect( d->mItemSyncer, SIGNAL(result(KJob*)), SLOT(slotItemSyncDone(KJob*)) );
00483 }
00484 d->mItemSyncer->setIncrementalSyncItems( changedItems, removedItems );
00485 }
00486
00487 void ResourceBasePrivate::slotItemSyncDone( KJob *job )
00488 {
00489 mItemSyncer = 0;
00490 Q_Q( ResourceBase );
00491 if ( job->error() ) {
00492 emit q->error( job->errorString() );
00493 }
00494 if ( scheduler->isEmpty() )
00495 emit q->status( AgentBase::Idle );
00496 scheduler->taskDone();
00497 }
00498
00499 void ResourceBasePrivate::slotPercent( KJob *job, unsigned long percent )
00500 {
00501 Q_Q( ResourceBase );
00502 Q_UNUSED( job );
00503 emit q->percent( percent );
00504 }
00505
00506 #include "resourcebase.moc"