delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / runtime / nepomuk / services / storage / repository.cpp
blob57606d47fd27f567f93421792138bb3073525dda
1 /*
3 * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
5 * This file is part of the Nepomuk KDE project.
6 * Copyright (C) 2006-2008 Sebastian Trueg <trueg@kde.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 * See the file "COPYING" for the exact licensing terms.
15 #include "repository.h"
16 #include "nepomukstorage-config.h"
17 #include "modelcopyjob.h"
19 #ifdef HAVE_CLUCENE
20 #include "cluceneanalyzer.h"
21 #endif
23 #include <Soprano/Backend>
24 #include <Soprano/Global>
25 #include <Soprano/Version>
26 #include <Soprano/StorageModel>
27 #include <Soprano/Error/Error>
28 #include <Soprano/Vocabulary/Xesam>
29 #include <Soprano/Vocabulary/RDF>
31 #ifdef HAVE_SOPRANO_INDEX
32 #include <Soprano/Index/IndexFilterModel>
33 #include <Soprano/Index/CLuceneIndex>
34 #endif
36 #include <KStandardDirs>
37 #include <KDebug>
38 #include <KConfigGroup>
39 #include <KSharedConfig>
40 #include <KLocale>
41 #include <KNotification>
42 #include <KIcon>
44 #include <QtCore/QTimer>
45 #include <QtCore/QThread>
46 #include <QtCore/QCoreApplication>
49 namespace {
50 QString createStoragePath( const QString& repositoryId )
52 return KStandardDirs::locateLocal( "data", "nepomuk/repository/" + repositoryId + "/" );
55 #if defined(HAVE_SOPRANO_INDEX) && defined(HAVE_CLUCENE) && SOPRANO_IS_VERSION(2,1,64)
56 class RebuildIndexThread : public QThread
58 public:
59 RebuildIndexThread( Soprano::Index::IndexFilterModel* model )
60 : m_model( model ) {
63 void run() {
64 m_model->rebuildIndex();
67 private:
68 Soprano::Index::IndexFilterModel* m_model;
70 #endif
74 Nepomuk::Repository::Repository( const QString& name )
75 : m_name( name ),
76 m_state( CLOSED ),
77 m_model( 0 ),
78 m_analyzer( 0 ),
79 m_index( 0 ),
80 m_indexModel( 0 )
85 Nepomuk::Repository::~Repository()
87 close();
91 void Nepomuk::Repository::close()
93 if ( m_state == OPEN ) {
94 #ifdef HAVE_SOPRANO_INDEX
95 delete m_indexModel;
96 delete m_index;
97 m_indexModel = 0;
98 m_index = 0;
99 #ifdef HAVE_CLUCENE
100 delete m_analyzer;
101 m_analyzer = 0;
102 #endif
103 #endif
104 delete m_model;
105 m_model = 0;
107 m_state = CLOSED;
112 void Nepomuk::Repository::open()
114 Q_ASSERT( m_state == CLOSED );
116 m_state = OPENING;
118 // get used backend
119 // =================================
120 const Soprano::Backend* backend = activeSopranoBackend();
121 if ( !backend ) {
122 m_state = CLOSED;
123 emit opened( this, false );
124 return;
127 // read config
128 // =================================
129 KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
130 QString oldBackendName = repoConfig.readEntry( "Used Soprano Backend", backend->pluginName() );
131 QString oldBasePath = repoConfig.readPathEntry( "Storage Dir", QString() ); // backward comp: empty string means old storage path
133 // If possible we want to keep the old storage path. exception: oldStoragePath is empty. In that case we stay backwards
134 // compatible and convert the data to the new default location createStoragePath( name ) + "data/" + backend->pluginName()
136 // If we have a proper oldStoragePath and a different backend we use the oldStoragePath as basePath
137 // newDataPath = oldStoragePath + "data/" + backend->pluginName()
138 // oldDataPath = oldStoragePath + "data/" + oldBackendName
141 // create storage paths
142 // =================================
143 m_basePath = oldBasePath.isEmpty() ? createStoragePath( name() ) : oldBasePath;
144 QString indexPath = m_basePath + "index";
145 QString storagePath = m_basePath + "data/" + backend->pluginName();
147 KStandardDirs::makeDir( indexPath );
148 KStandardDirs::makeDir( storagePath );
150 kDebug(300002) << "opening repository '" << name() << "' at '" << m_basePath << "'";
153 // open storage
154 // =================================
155 m_model = backend->createModel( QList<Soprano::BackendSetting>() << Soprano::BackendSetting( Soprano::BackendOptionStorageDir, storagePath ) );
156 if ( !m_model ) {
157 kDebug(300002) << "Unable to create model for repository" << name();
158 m_state = CLOSED;
159 emit opened( this, false );
160 return;
163 kDebug(300002) << "Successfully created new model for repository" << name();
165 #if defined(HAVE_SOPRANO_INDEX) && defined(HAVE_CLUCENE)
166 m_analyzer = new CLuceneAnalyzer();
167 m_index = new Soprano::Index::CLuceneIndex( m_analyzer );
169 if ( m_index->open( indexPath, true ) ) {
170 kDebug(300002) << "Successfully created new index for repository" << name();
171 m_indexModel = new Soprano::Index::IndexFilterModel( m_index, m_model );
173 // FIXME: find a good value here
174 m_indexModel->setTransactionCacheSize( 100 );
176 #if SOPRANO_IS_VERSION(2,0,99)
177 // no need for the whole content in the store, we only need it for searching
178 // (compare the strigi backend)
179 m_indexModel->addIndexOnlyPredicate( Soprano::Vocabulary::Xesam::asText() );
180 #endif
181 #if SOPRANO_IS_VERSION(2,1,64)
182 m_indexModel->addForceIndexPredicate( Soprano::Vocabulary::RDF::type() );
183 #endif
185 setParentModel( m_indexModel );
187 else {
188 kDebug(300002) << "Unable to open CLucene index for repo '" << name() << "': " << m_index->lastError();
189 delete m_index;
190 delete m_model;
191 m_index = 0;
192 m_model = 0;
194 m_state = CLOSED;
195 emit opened( this, false );
196 return;
198 #else
199 setParentModel( m_model );
200 #endif
202 // check if we have to convert
203 // =================================
204 bool convertingData = false;
206 // if the backend changed we convert
207 // in case only the storage dir changes we normally would not have to convert but
208 // it is just simpler this way
209 if ( oldBackendName != backend->pluginName() ||
210 oldBasePath.isEmpty() ) {
212 kDebug() << "Previous backend:" << oldBackendName << "- new backend:" << backend->pluginName();
213 kDebug() << "Old path:" << oldBasePath << "- new path:" << m_basePath;
215 if ( oldBasePath.isEmpty() ) {
216 // backward comp: empty string means old storage path
217 // and before we stored the data directly in the default basePath
218 m_oldStoragePath = createStoragePath( name() );
220 else {
221 m_oldStoragePath = m_basePath + "data/" + oldBackendName;
224 // try creating a model for the old storage
225 Soprano::Model* oldModel = 0;
226 m_oldStorageBackend = Soprano::discoverBackendByName( oldBackendName );
227 if ( m_oldStorageBackend ) {
228 // FIXME: even if there is no old data we still create a model here which results in a new empty db!
229 oldModel = m_oldStorageBackend->createModel( QList<Soprano::BackendSetting>() << Soprano::BackendSetting( Soprano::BackendOptionStorageDir, m_oldStoragePath ) );
232 if ( oldModel ) {
233 if ( !oldModel->isEmpty() ) {
234 kDebug() << "Starting model conversion";
236 convertingData = true;
237 // No need to use the index filter as it already contains the data
238 ModelCopyJob* copyJob = new ModelCopyJob( oldModel, m_model, this );
239 connect( copyJob, SIGNAL( result( KJob* ) ), this, SLOT( copyFinished( KJob* ) ) );
240 copyJob->start();
242 else {
243 m_state = OPEN;
246 else {
247 // FIXME: inform the user
248 kDebug( 300002 ) << "Unable to convert old model.";
249 m_state = OPEN;
252 else {
253 kDebug() << "no need to convert" << name();
254 m_state = OPEN;
257 // save the settings
258 // =================================
259 // do not save when converting yet. If converting is cancelled we would loose data.
260 // this way conversion is restarted the next time
261 if ( !convertingData ) {
262 repoConfig.writeEntry( "Used Soprano Backend", backend->pluginName() );
263 repoConfig.writePathEntry( "Storage Dir", m_basePath );
264 repoConfig.sync(); // even if we crash the model has been created
266 if( m_state == OPEN ) {
267 if ( !rebuildIndexIfNecessary() ) {
268 emit opened( this, true );
272 else {
273 KNotification::event( "convertingNepomukData",
274 i18nc("@info - notification message",
275 "Converting Nepomuk data to a new backend. This might take a while."),
276 KIcon( "nepomuk" ).pixmap( 32, 32 ) );
281 void Nepomuk::Repository::rebuildingIndexFinished()
283 KNotification::event( "rebuldingNepomukIndexDone",
284 i18nc("@info - notification message",
285 "Rebuilding Nepomuk full text search index for new features done."),
286 KIcon( "nepomuk" ).pixmap( 32, 32 ) );
288 // save our new settings
289 KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
290 repoConfig.writeEntry( "rebuilt index for type indexing", true );
292 // inform that we are open and done
293 m_state = OPEN;
294 emit opened( this, true );
298 void Nepomuk::Repository::copyFinished( KJob* job )
300 if ( job->error() ) {
301 KNotification::event( "convertingNepomukDataFailed",
302 i18nc("@info - notification message",
303 "Converting Nepomuk data to the new backend failed. Data may still be recovered manually though."),
304 KIcon( "nepomuk" ).pixmap( 32, 32 ) );
306 kDebug( 300002 ) << "Converting old model failed.";
308 else {
309 KNotification::event( "convertingNepomukDataDone",
310 i18nc("@info - notification message",
311 "Successfully converted Nepomuk data to the new backend."),
312 KIcon( "nepomuk" ).pixmap( 32, 32 ) );
314 kDebug() << "Successfully converted model data for repo" << name();
316 // delete the old model
317 ModelCopyJob* copyJob = qobject_cast<ModelCopyJob*>( job );
318 delete copyJob->source();
320 // cleanup the actual data
321 m_oldStorageBackend->deleteModelData( QList<Soprano::BackendSetting>() << Soprano::BackendSetting( Soprano::BackendOptionStorageDir, m_oldStoragePath ) );
323 // save our new settings
324 KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
325 repoConfig.writeEntry( "Used Soprano Backend", activeSopranoBackend()->pluginName() );
326 repoConfig.writePathEntry( "Storage Dir", m_basePath );
327 repoConfig.sync();
329 if ( rebuildIndexIfNecessary() ) {
330 // opened will be emitted in rebuildingIndexFinished
331 return;
335 // although converting might have failed, the new model is open anyway
336 m_state = OPEN;
337 emit opened( this, true );
341 void Nepomuk::Repository::optimize()
343 QTimer::singleShot( 0, this, SLOT( slotDoOptimize() ) );
347 void Nepomuk::Repository::slotDoOptimize()
349 #ifdef HAVE_SOPRANO_INDEX
350 #if SOPRANO_IS_VERSION(2,1,60)
351 m_index->optimize();
352 #endif
353 #endif
357 bool Nepomuk::Repository::rebuildIndexIfNecessary()
359 #if defined(HAVE_SOPRANO_INDEX) && defined(HAVE_CLUCENE) && SOPRANO_IS_VERSION(2,1,64)
360 KConfigGroup repoConfig = KSharedConfig::openConfig( "nepomukserverrc" )->group( name() + " Settings" );
361 if( !repoConfig.readEntry( "rebuilt index for type indexing", false ) ) {
362 KNotification::event( "rebuldingNepomukIndex",
363 i18nc("@info - notification message",
364 "Rebuilding Nepomuk full text search index for new features. This will only be done once and might take a while."),
365 KIcon( "nepomuk" ).pixmap( 32, 32 ) );
366 RebuildIndexThread* rit = new RebuildIndexThread( m_indexModel );
367 connect( rit, SIGNAL( finished() ), this, SLOT( rebuildingIndexFinished() ) );
368 connect( rit, SIGNAL( finished() ), rit, SLOT( deleteLater() ) );
369 rit->start();
370 return true;
372 #endif
373 return false;
377 const Soprano::Backend* Nepomuk::Repository::activeSopranoBackend()
379 QString backendName = KSharedConfig::openConfig( "nepomukserverrc" )->group( "Basic Settings" ).readEntry( "Soprano Backend", "sesame2" );
380 const Soprano::Backend* backend = ::Soprano::discoverBackendByName( backendName );
381 if ( !backend ) {
382 kDebug(300002) << "(Nepomuk::Core::Core) could not find backend" << backendName << ". Falling back to default.";
383 backend = ::Soprano::usedBackend();
385 if ( !backend ) {
386 kDebug(300002) << "(Nepomuk::Core::Core) could not find a backend.";
388 return backend;
391 #include "repository.moc"