1
/* This file is part of the KDE Project
2
Copyright (c) 2009-2011 Sebastian Trueg <trueg@kde.org>
4
This library is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Library General Public
6
License version 2 as published by the Free Software Foundation.
8
This library is distributed in the hope that it will be useful,
9
but WITHOUT ANY WARRANTY; without even the implied warranty of
10
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
Library General Public License for more details.
13
You should have received a copy of the GNU Library General Public License
14
along with this library; see the file COPYING.LIB. If not, write to
15
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16
Boston, MA 02110-1301, USA.
19
#include "metadatamover.h"
20
#include "nepomukfilewatch.h"
21
#include "datamanagement.h"
23
#include <QtCore/QTimer>
25
#include <Soprano/Model>
26
#include <Soprano/Node>
27
#include <Soprano/NodeIterator>
28
#include <Soprano/QueryResultIterator>
29
#include <Soprano/LiteralValue>
31
#include <Nepomuk/Vocabulary/NIE>
36
Nepomuk::MetadataMover::MetadataMover( Soprano::Model* model, QObject* parent )
38
m_queueMutex(QMutex::Recursive),
41
// setup the main update queue timer
42
m_queueTimer = new QTimer(this);
43
connect(m_queueTimer, SIGNAL(timeout()),
44
this, SLOT(slotWorkUpdateQueue()),
45
Qt::DirectConnection);
47
// setup the cleanup timer which removes requests that are done
48
m_recentlyFinishedRequestsTimer = new QTimer(this);
49
connect( m_recentlyFinishedRequestsTimer, SIGNAL( timeout() ),
50
this, SLOT( slotClearRecentlyFinishedRequests() ),
51
Qt::DirectConnection );
52
m_recentlyFinishedRequestsTimer->setInterval( 30000 );
56
Nepomuk::MetadataMover::~MetadataMover()
61
void Nepomuk::MetadataMover::moveFileMetadata( const KUrl& from, const KUrl& to )
63
// kDebug() << from << to;
64
Q_ASSERT( !from.path().isEmpty() && from.path() != "/" );
65
Q_ASSERT( !to.path().isEmpty() && to.path() != "/" );
67
QMutexLocker lock(&m_queueMutex);
69
UpdateRequest req( from, to );
70
if ( !m_updateQueue.contains( req ) &&
71
!m_recentlyFinishedRequests.contains( req ) )
72
m_updateQueue.enqueue( req );
74
QTimer::singleShot(0, this, SLOT(slotStartUpdateTimer()));
78
void Nepomuk::MetadataMover::removeFileMetadata( const KUrl& file )
80
Q_ASSERT( !file.path().isEmpty() && file.path() != "/" );
81
removeFileMetadata( KUrl::List() << file );
85
void Nepomuk::MetadataMover::removeFileMetadata( const KUrl::List& files )
88
QMutexLocker lock(&m_queueMutex);
90
foreach( const KUrl& file, files ) {
91
UpdateRequest req( file );
92
if ( !m_updateQueue.contains( req ) &&
93
!m_recentlyFinishedRequests.contains( req ) )
94
m_updateQueue.enqueue( req );
97
QTimer::singleShot(0, this, SLOT(slotStartUpdateTimer()));
101
void Nepomuk::MetadataMover::slotWorkUpdateQueue()
103
// lock for initial iteration
104
QMutexLocker lock(&m_queueMutex);
107
if( !m_updateQueue.isEmpty() ) {
108
UpdateRequest updateRequest = m_updateQueue.dequeue();
109
m_recentlyFinishedRequests.insert( updateRequest );
111
// unlock after queue utilization
114
// kDebug() << "========================= handling" << updateRequest.source() << updateRequest.target();
116
// an empty second url means deletion
117
if( updateRequest.target().isEmpty() ) {
118
removeMetadata( updateRequest.source() );
121
const KUrl from = updateRequest.source();
122
const KUrl to = updateRequest.target();
124
// We do NOT get deleted messages for overwritten files! Thus, we
125
// have to remove all metadata for overwritten files first.
126
removeMetadata( to );
128
// and finally update the old statements
129
updateMetadata( from, to );
132
// kDebug() << "========================= done with" << updateRequest.source() << updateRequest.target();
135
kDebug() << "All update requests handled. Stopping timer.";
136
m_queueTimer->stop();
141
void Nepomuk::MetadataMover::removeMetadata( const KUrl& url )
145
if ( url.isEmpty() ) {
146
kDebug() << "empty path. Looks like a bug somewhere...";
149
const bool isFolder = url.url().endsWith('/');
150
Nepomuk::removeResources(QList<QUrl>() << url);
154
// Recursively remove children
155
// We cannot use the nie:isPartOf relation since only children could have metadata. Thus, we do a regex
156
// match on all files and folders below the URL we got.
158
// CAUTION: The trailing slash on the from URL is essential! Otherwise we might match the newly added
159
// URLs, too (in case a rename only added chars to the name)
161
const QString query = QString::fromLatin1( "select distinct ?r where { "
163
"FILTER(REGEX(STR(?url),'^%2')) . "
165
.arg( Soprano::Node::resourceToN3( Nepomuk::Vocabulary::NIE::url() ),
166
url.url(KUrl::AddTrailingSlash) );
169
// We cannot use one big loop since our updateMetadata calls below can change the iterator
170
// which could have bad effects like row skipping. Thus, we handle the urls in chunks of
175
Soprano::QueryResultIterator it = m_model->executeQuery( query + QLatin1String( " LIMIT 20" ),
176
Soprano::Query::QueryLanguageSparql );
180
if ( !urls.isEmpty() ) {
181
Nepomuk::removeResources(urls);
192
void Nepomuk::MetadataMover::updateMetadata( const KUrl& from, const KUrl& to )
194
kDebug() << from << "->" << to;
196
if ( m_model->executeQuery(QString::fromLatin1("ask where { { %1 ?p ?o . } UNION { ?r nie:url %1 . } . }")
197
.arg(Soprano::Node::resourceToN3(from)),
198
Soprano::Query::QueryLanguageSparql).boolValue() ) {
199
Nepomuk::setProperty(QList<QUrl>() << from, Nepomuk::Vocabulary::NIE::url(), QVariantList() << to);
203
// If we have no metadata yet we need to tell the file indexer (if running) so it can
204
// create the metadata in case the target folder is configured to be indexed.
206
emit movedWithoutData( to.path() );
211
// removes all finished requests older than 1 minute
212
void Nepomuk::MetadataMover::slotClearRecentlyFinishedRequests()
214
QMutexLocker lock( &m_queueMutex );
215
QSet<UpdateRequest>::iterator it = m_recentlyFinishedRequests.begin();
216
while ( it != m_recentlyFinishedRequests.end() ) {
217
const UpdateRequest& req = *it;
218
if ( req.timestamp().secsTo( QDateTime::currentDateTime() ) > 60 ) {
219
it = m_recentlyFinishedRequests.erase( it );
226
if(m_recentlyFinishedRequests.isEmpty()) {
227
kDebug() << "No more old requests. Stopping timer.";
228
m_recentlyFinishedRequestsTimer->stop();
233
// start the timer in the update thread
234
void Nepomuk::MetadataMover::slotStartUpdateTimer()
236
if(!m_queueTimer->isActive()) {
237
m_queueTimer->start();
239
if(!m_recentlyFinishedRequestsTimer->isActive()) {
240
m_recentlyFinishedRequestsTimer->start();
244
#include "metadatamover.moc"