• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

NepomukDaemons

repository.cpp

Go to the documentation of this file.
00001 /*
00002  *
00003  * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
00004  *
00005  * This file is part of the Nepomuk KDE project.
00006  * Copyright (C) 2006-2008 Sebastian Trueg <trueg@kde.org>
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  * See the file "COPYING" for the exact licensing terms.
00013  */
00014 
00015 #include "repository.h"
00016 #include "nepomukstorage-config.h"
00017 #include "modelcopyjob.h"
00018 
00019 #ifdef HAVE_CLUCENE
00020 #include "cluceneanalyzer.h"
00021 #endif
00022 
00023 #include <Soprano/Backend>
00024 #include <Soprano/Global>
00025 #include <Soprano/Version>
00026 #include <Soprano/StorageModel>
00027 #include <Soprano/Error/Error>
00028 #include <Soprano/Vocabulary/Xesam>
00029 #include <Soprano/Vocabulary/RDF>
00030 
00031 #ifdef HAVE_SOPRANO_INDEX
00032 #include <Soprano/Index/IndexFilterModel>
00033 #include <Soprano/Index/CLuceneIndex>
00034 #endif
00035 
00036 #include <KStandardDirs>
00037 #include <KDebug>
00038 #include <KConfigGroup>
00039 #include <KSharedConfig>
00040 #include <KLocale>
00041 #include <KNotification>
00042 #include <KIcon>
00043 
00044 #include <QtCore/QTimer>
00045 #include <QtCore/QThread>
00046 #include <QtCore/QCoreApplication>
00047 
00048 
00049 namespace {
00050     QString createStoragePath( const QString& repositoryId )
00051     {
00052         return KStandardDirs::locateLocal( "data", "nepomuk/repository/" + repositoryId + "/" );
00053     }
00054 
00055 #if defined(HAVE_SOPRANO_INDEX) && defined(HAVE_CLUCENE) && SOPRANO_IS_VERSION(2,1,64)
00056     class RebuildIndexThread : public QThread
00057     {
00058     public:
00059         RebuildIndexThread( Soprano::Index::IndexFilterModel* model )
00060             : m_model( model ) {
00061         }
00062 
00063         void run() {
00064             m_model->rebuildIndex();
00065         }
00066 
00067     private:
00068         Soprano::Index::IndexFilterModel* m_model;
00069     };
00070 #endif
00071 }
00072 
00073 
00074 Nepomuk::Repository::Repository( const QString& name )
00075     : m_name( name ),
00076       m_state( CLOSED ),
00077       m_model( 0 ),
00078       m_analyzer( 0 ),
00079       m_index( 0 ),
00080       m_indexModel( 0 )
00081 {
00082 }
00083 
00084 
00085 Nepomuk::Repository::~Repository()
00086 {
00087     close();
00088 }
00089 
00090 
00091 void Nepomuk::Repository::close()
00092 {
00093     if ( m_state == OPEN ) {
00094 #ifdef HAVE_SOPRANO_INDEX
00095         delete m_indexModel;
00096         delete m_index;
00097         m_indexModel = 0;
00098         m_index = 0;
00099 #ifdef HAVE_CLUCENE
00100         delete m_analyzer;
00101         m_analyzer = 0;
00102 #endif
00103 #endif
00104         delete m_model;
00105         m_model = 0;
00106 
00107         m_state = CLOSED;
00108     }
00109 }
00110 
00111 
00112 void Nepomuk::Repository::open()
00113 {
00114     Q_ASSERT( m_state == CLOSED );
00115 
00116     m_state = OPENING;
00117 
00118     // get used backend
00119     // =================================
00120     const Soprano::Backend* backend = activeSopranoBackend();
00121     if ( !backend ) {
00122         m_state = CLOSED;
00123         emit opened( this, false );
00124         return;
00125     }
00126 
00127     // read config
00128     // =================================
00129     KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
00130     QString oldBackendName = repoConfig.readEntry( "Used Soprano Backend", backend->pluginName() );
00131     QString oldBasePath = repoConfig.readPathEntry( "Storage Dir", QString() ); // backward comp: empty string means old storage path
00132 
00133     // If possible we want to keep the old storage path. exception: oldStoragePath is empty. In that case we stay backwards
00134     // compatible and convert the data to the new default location createStoragePath( name ) + "data/" + backend->pluginName()
00135     //
00136     // If we have a proper oldStoragePath and a different backend we use the oldStoragePath as basePath
00137     // newDataPath = oldStoragePath + "data/" + backend->pluginName()
00138     // oldDataPath = oldStoragePath + "data/" + oldBackendName
00139 
00140 
00141     // create storage paths
00142     // =================================
00143     m_basePath = oldBasePath.isEmpty() ? createStoragePath( name() ) : oldBasePath;
00144     QString indexPath = m_basePath + "index";
00145     QString storagePath = m_basePath + "data/" + backend->pluginName();
00146 
00147     KStandardDirs::makeDir( indexPath );
00148     KStandardDirs::makeDir( storagePath );
00149 
00150     kDebug(300002) << "opening repository '" << name() << "' at '" << m_basePath << "'";
00151 
00152 
00153     // open storage
00154     // =================================
00155     m_model = backend->createModel( QList<Soprano::BackendSetting>() << Soprano::BackendSetting( Soprano::BackendOptionStorageDir, storagePath ) );
00156     if ( !m_model ) {
00157         kDebug(300002) << "Unable to create model for repository" << name();
00158         m_state = CLOSED;
00159         emit opened( this, false );
00160         return;
00161     }
00162 
00163     kDebug(300002) << "Successfully created new model for repository" << name();
00164 
00165 #if defined(HAVE_SOPRANO_INDEX) && defined(HAVE_CLUCENE)
00166     m_analyzer = new CLuceneAnalyzer();
00167     m_index = new Soprano::Index::CLuceneIndex( m_analyzer );
00168 
00169     if ( m_index->open( indexPath, true ) ) {
00170         kDebug(300002) << "Successfully created new index for repository" << name();
00171         m_indexModel = new Soprano::Index::IndexFilterModel( m_index, m_model );
00172 
00173         // FIXME: find a good value here
00174         m_indexModel->setTransactionCacheSize( 100 );
00175 
00176 #if SOPRANO_IS_VERSION(2,0,99)
00177         // no need for the whole content in the store, we only need it for searching
00178         // (compare the strigi backend)
00179         m_indexModel->addIndexOnlyPredicate( Soprano::Vocabulary::Xesam::asText() );
00180 #endif
00181 #if SOPRANO_IS_VERSION(2,1,64)
00182         m_indexModel->addForceIndexPredicate( Soprano::Vocabulary::RDF::type() );
00183 #endif
00184 
00185         setParentModel( m_indexModel );
00186     }
00187     else {
00188         kDebug(300002) << "Unable to open CLucene index for repo '" << name() << "': " << m_index->lastError();
00189         delete m_index;
00190         delete m_model;
00191         m_index = 0;
00192         m_model = 0;
00193 
00194         m_state = CLOSED;
00195         emit opened( this, false );
00196         return;
00197     }
00198 #else
00199     setParentModel( m_model );
00200 #endif
00201 
00202     // check if we have to convert
00203     // =================================
00204     bool convertingData = false;
00205 
00206     // if the backend changed we convert
00207     // in case only the storage dir changes we normally would not have to convert but
00208     // it is just simpler this way
00209     if ( oldBackendName != backend->pluginName() ||
00210          oldBasePath.isEmpty() ) {
00211 
00212         kDebug() << "Previous backend:" << oldBackendName << "- new backend:" << backend->pluginName();
00213         kDebug() << "Old path:" << oldBasePath << "- new path:" << m_basePath;
00214 
00215         if ( oldBasePath.isEmpty() ) {
00216             // backward comp: empty string means old storage path
00217             // and before we stored the data directly in the default basePath
00218             m_oldStoragePath = createStoragePath( name() );
00219         }
00220         else {
00221             m_oldStoragePath = m_basePath + "data/" + oldBackendName;
00222         }
00223 
00224         // try creating a model for the old storage
00225         Soprano::Model* oldModel = 0;
00226         m_oldStorageBackend = Soprano::discoverBackendByName( oldBackendName );
00227         if ( m_oldStorageBackend ) {
00228             // FIXME: even if there is no old data we still create a model here which results in a new empty db!
00229             oldModel = m_oldStorageBackend->createModel( QList<Soprano::BackendSetting>() << Soprano::BackendSetting( Soprano::BackendOptionStorageDir, m_oldStoragePath ) );
00230         }
00231 
00232         if ( oldModel ) {
00233             if ( !oldModel->isEmpty() ) {
00234                 kDebug() << "Starting model conversion";
00235 
00236                 convertingData = true;
00237                 // No need to use the index filter as it already contains the data
00238                 ModelCopyJob* copyJob = new ModelCopyJob( oldModel, m_model, this );
00239                 connect( copyJob, SIGNAL( result( KJob* ) ), this, SLOT( copyFinished( KJob* ) ) );
00240                 copyJob->start();
00241             }
00242             else {
00243                 m_state = OPEN;
00244             }
00245         }
00246         else {
00247             // FIXME: inform the user
00248             kDebug( 300002 ) << "Unable to convert old model.";
00249             m_state = OPEN;
00250         }
00251     }
00252     else {
00253         kDebug() << "no need to convert" << name();
00254         m_state = OPEN;
00255     }
00256 
00257     // save the settings
00258     // =================================
00259     // do not save when converting yet. If converting is cancelled we would loose data.
00260     // this way conversion is restarted the next time
00261     if ( !convertingData ) {
00262         repoConfig.writeEntry( "Used Soprano Backend", backend->pluginName() );
00263         repoConfig.writePathEntry( "Storage Dir", m_basePath );
00264         repoConfig.sync(); // even if we crash the model has been created
00265 
00266         if( m_state == OPEN ) {
00267             if ( !rebuildIndexIfNecessary() ) {
00268                 emit opened( this, true );
00269             }
00270         }
00271     }
00272     else {
00273         KNotification::event( "convertingNepomukData",
00274                               i18nc("@info - notification message",
00275                                     "Converting Nepomuk data to a new backend. This might take a while."),
00276                                     KIcon( "nepomuk" ).pixmap( 32, 32 ) );
00277     }
00278 }
00279 
00280 
00281 void Nepomuk::Repository::rebuildingIndexFinished()
00282 {
00283     KNotification::event( "rebuldingNepomukIndexDone",
00284                           i18nc("@info - notification message",
00285                                 "Rebuilding Nepomuk full text search index for new features done."),
00286                           KIcon( "nepomuk" ).pixmap( 32, 32 ) );
00287 
00288     // save our new settings
00289     KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
00290     repoConfig.writeEntry( "rebuilt index for type indexing", true );
00291 
00292     // inform that we are open and done
00293     m_state = OPEN;
00294     emit opened( this, true );
00295 }
00296 
00297 
00298 void Nepomuk::Repository::copyFinished( KJob* job )
00299 {
00300     if ( job->error() ) {
00301         KNotification::event( "convertingNepomukDataFailed",
00302                               i18nc("@info - notification message",
00303                                     "Converting Nepomuk data to the new backend failed. Data may still be recovered manually though."),
00304                                     KIcon( "nepomuk" ).pixmap( 32, 32 ) );
00305 
00306         kDebug( 300002 ) << "Converting old model failed.";
00307     }
00308     else {
00309         KNotification::event( "convertingNepomukDataDone",
00310                               i18nc("@info - notification message",
00311                                     "Successfully converted Nepomuk data to the new backend."),
00312                                     KIcon( "nepomuk" ).pixmap( 32, 32 ) );
00313 
00314         kDebug() << "Successfully converted model data for repo" << name();
00315 
00316         // delete the old model
00317         ModelCopyJob* copyJob = qobject_cast<ModelCopyJob*>( job );
00318         delete copyJob->source();
00319 
00320         // cleanup the actual data
00321         m_oldStorageBackend->deleteModelData( QList<Soprano::BackendSetting>() << Soprano::BackendSetting( Soprano::BackendOptionStorageDir, m_oldStoragePath ) );
00322 
00323         // save our new settings
00324         KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
00325         repoConfig.writeEntry( "Used Soprano Backend", activeSopranoBackend()->pluginName() );
00326         repoConfig.writePathEntry( "Storage Dir", m_basePath );
00327         repoConfig.sync();
00328 
00329         if ( rebuildIndexIfNecessary() ) {
00330             // opened will be emitted in rebuildingIndexFinished
00331             return;
00332         }
00333     }
00334 
00335     // although converting might have failed, the new model is open anyway
00336     m_state = OPEN;
00337     emit opened( this, true );
00338 }
00339 
00340 
00341 void Nepomuk::Repository::optimize()
00342 {
00343     QTimer::singleShot( 0, this, SLOT( slotDoOptimize() ) );
00344 }
00345 
00346 
00347 void Nepomuk::Repository::slotDoOptimize()
00348 {
00349 #ifdef HAVE_SOPRANO_INDEX
00350 #if SOPRANO_IS_VERSION(2,1,60)
00351     m_index->optimize();
00352 #endif
00353 #endif
00354 }
00355 
00356 
00357 bool Nepomuk::Repository::rebuildIndexIfNecessary()
00358 {
00359 #if defined(HAVE_SOPRANO_INDEX) && defined(HAVE_CLUCENE) && SOPRANO_IS_VERSION(2,1,64)
00360     KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
00361     if( !repoConfig.readEntry( "rebuilt index for type indexing", false ) ) {
00362         KNotification::event( "rebuldingNepomukIndex",
00363                               i18nc("@info - notification message",
00364                                     "Rebuilding Nepomuk full text search index for new features. This will only be done once and might take a while."),
00365                                     KIcon( "nepomuk" ).pixmap( 32, 32 ) );
00366         RebuildIndexThread* rit = new RebuildIndexThread( m_indexModel );
00367         connect( rit, SIGNAL( finished() ), this, SLOT( rebuildingIndexFinished() ) );
00368         connect( rit, SIGNAL( finished() ), rit, SLOT( deleteLater() ) );
00369         rit->start();
00370         return true;
00371     }
00372 #endif
00373     return false;
00374 }
00375 
00376 
00377 const Soprano::Backend* Nepomuk::Repository::activeSopranoBackend()
00378 {
00379     QString backendName = KSharedConfig::openConfig( "nepomukserverrc" )->group( "Basic Settings" ).readEntry( "Soprano Backend", "sesame2" );
00380     const Soprano::Backend* backend = ::Soprano::discoverBackendByName( backendName );
00381     if ( !backend ) {
00382         kDebug(300002) << "(Nepomuk::Core::Core) could not find backend" << backendName << ". Falling back to default.";
00383         backend = ::Soprano::usedBackend();
00384     }
00385     if ( !backend ) {
00386         kDebug(300002) << "(Nepomuk::Core::Core) could not find a backend.";
00387     }
00388     return backend;
00389 }
00390 
00391 #include "repository.moc"

NepomukDaemons

Skip menu "NepomukDaemons"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KCMShell
  • KNotify
  • KStyles
  • Nepomuk Daemons
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal