Don't keep compiling/run if something failed.
[kdevelopdvcssupport.git] / plugins / teamwork / messagehistorymanager.cpp
blob3f5de9f213e33d40512d1caf25be063ccd96d7c7
1 /***************************************************************************
2 Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
3 ***************************************************************************/
5 /***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 ***************************************************************************/
15 #include "messagehistorymanager.h"
16 #include <fstream>
17 #include <QDate>
18 #include <QMap>
19 #include <QTimer>
20 #include <kurl.h>
21 #include <klockfile.h>
22 #include <kio/netaccess.h>
24 #include "lib/network/serialization.h"
25 #include "lib/network/messageserialization.h"
27 #include "kdevteamwork_messages.h"
28 #include "kdevteamwork_user.h"
29 #include "teamworkfoldermanager.h"
32 #include <boost/serialization/vector.hpp>
33 #include <boost/serialization/map.hpp>
34 #include <boost/archive/xml_iarchive.hpp>
35 #include <boost/archive/xml_oarchive.hpp>
36 #include <boost/archive/binary_iarchive.hpp>
37 #include <boost/archive/binary_oarchive.hpp>
38 #include <boost/serialization/level.hpp>
40 #include <QDir>
41 #include "kdevteamwork_client.h"
42 //#include <map>
44 using namespace Teamwork;
46 struct FileEntryHeader {
47 int version;
48 int messages;
49 FileEntryHeader( int _messages=0 ) : version(1), messages(_messages) {
52 void read( istream& str ) {
53 str >> version >> messages;
55 void write( ostream& str ) {
56 str << version << endl << messages << endl;
60 HistoryMessageDesc::HistoryMessageDesc() {
63 HistoryMessageDesc::HistoryMessageDesc( const HistoryMessagePointer& msg )
65 message = msg;
66 HistoryMessagePointer::Locked l = msg;
67 if( l ) {
68 isIncoming = l->info().isIncoming();
70 UserPointer::Locked lu = userFromSession( l->info().session() );
71 if( lu ) {
72 user = lu->identity();
73 } else {
75 } else {
79 MessageHistoryManager::MessageHistoryManager( Teamwork::LoggerPointer logger ) : m_logger(logger) {
80 m_pendingTimer = new QTimer();
81 m_pendingTimer->start( 1000 );
82 m_lockFile = new KLockFile( lockFileName() );
83 connect( m_pendingTimer, SIGNAL( timeout() ), this, SLOT( writePending() ) );
86 MessageHistoryManager::~MessageHistoryManager() {
89 void MessageHistoryManager::addMessage( KDevTeamworkTextMessage* msg ) {
90 HistoryMessageDesc m( msg );
91 m_pending << m;
92 emit newMessage( msg );
95 QList< HistoryMessagePointer > MessageHistoryManager::getMessages( const KDevTeamworkClientPointer& client, const QDate& from, const QDate& until, UserSet users ) {
96 writePending();
97 QStringList interestingFiles;
98 QList< HistoryMessageDesc > ret;
99 try {
100 KUrl kdir = directory();
102 if( !kdir.isLocalFile() )
103 throw QString( "directory \"%1\" is not local" ).arg( kdir.prettyUrl() );
105 QDir dir( kdir.toLocalFile() );
106 if( !dir.isReadable() ) throw QString( "directory \"%1\" is not readable" ).arg( dir.path() );
108 QStringList files = dir.entryList( QDir::Files );
110 for( QStringList::iterator it = files.begin(); it != files.end(); ++it ) {
111 QDate date = QDate::fromString( *it, Qt::ISODate );
112 if( !date.isValid() ) continue;
113 if( from.isValid() ) {
114 if( until.isValid() ) {
115 ///from "from" until "until"
116 if( date >= from && date <= until )
117 interestingFiles << *it;
118 } else {
119 ///from "from" until now
120 if( date >= from )
121 interestingFiles << *it;
123 } else {
124 if( until.isValid() ) {
125 ///from begin until "until"
126 if( date <= until )
127 interestingFiles << *it;
128 } else {
129 ///All messages
130 interestingFiles << *it;
135 for( QStringList::iterator it = interestingFiles.begin(); it != interestingFiles.end(); ++it ) {
136 KUrl fileUrl = kdir;
137 fileUrl.addPath( *it );
138 std::ifstream f( fileUrl.toLocalFile().toLatin1(), ios_base::binary );
139 if( !f.good() ) {
140 err() << "could not open" << ~fileUrl.toLocalFile();
142 while( !f.eof() ) {
143 if( !f.good() ) {
144 err() << "error while reading" << ~fileUrl.toLocalFile();
145 break;
148 FileEntryHeader header;
149 header.read( f );
151 //f.read( (char*)&header, sizeof( FileEntryHeader ) );
152 if( f.eof() ) break;
154 if( !f.good() ) {
155 err() << "could not read a header from file \"" << ~*it << "\"";
156 break;
157 } else {
158 out( Logger::Debug ) << "successfully read a header";
161 try {
162 ///read the message from the archive
163 boost::archive::xml_iarchive arch( f );
164 QList<HistoryMessageDesc> messages;
165 for( int a = 0; a < header.messages; a++ ) {
166 HistoryMessageDesc msg;
167 arch >> boost::serialization::make_nvp( "message", msg );
168 if( users.empty() || users.contains( msg.user ) )
169 ret << msg;
172 catch( std::exception& exc ) {
173 err() << "error while deserializing from \"" << ~*it << "\":" << exc.what();
174 break;
180 catch( QString str ) {
181 err() << "error in MessageHistoryManager::getMessages:" << ~str;
184 return fillMessageUsers( ret, client );
187 struct WaitError {
188 QString str;
189 WaitError( const QString& stri ) : str( stri ) {
193 void MessageHistoryManager::writePending()
197 KUrl dir = directory();
198 if( !dir.isLocalFile() ) throw QString( "the directory %1 is not local" ).arg( dir.prettyUrl() );
200 m_pendingTimer->start( 1000 );
201 QMap< QDate, QList<HistoryMessageDesc> > map;
202 std::map< Teamwork::UniqueMessageId, HistoryGroupLocation > insertions;
204 for( QList<HistoryMessageDesc>::iterator it = m_pending.begin(); it != m_pending.end(); ++it ) {
205 HistoryMessagePointer::Locked l = it->message;
206 if( l ) {
207 QDate key( l->creationTime().date() );
208 map[ key ] << *it;
209 } else {
210 err() << "could not lock a message for storing it into the history, the message will be lost";
214 for( QMap< QDate, QList<HistoryMessageDesc> >::iterator it = map.begin(); it != map.end(); ++it ) {
215 QDate date( it.key() );
217 if( !date.isValid() ) {
218 err() << "tried to store" << it->count() << "messages with invalid date";
219 continue;
222 if( !m_lockFile->isLocked() ) {
223 switch( m_lockFile->lock( KLockFile::NoBlockFlag | KLockFile::ForceFlag ) ) {
224 case KLockFile::LockOK:
225 break;
226 case KLockFile::LockFail:
227 throw WaitError( "locking the lockfile " + lockFileName() + " failed" );
228 break;
229 case KLockFile::LockError:
230 throw "an error occurred while locking the lockfile " + lockFileName();
231 break;
232 case KLockFile::LockStale:
233 throw WaitError( "the lockfile " + lockFileName() + " is stale" );
237 KUrl file = dir;
238 QString localFileName = date.toString( Qt::ISODate );
239 file.addPath( localFileName );
240 std::string fileName = ~file.toLocalFile();
242 if( fileName.empty() ) {
243 err() << "empty file-path for date" << ~date.toString( Qt::ISODate );
244 continue;
247 std::ofstream f( fileName.c_str(), ios_base::binary | ios_base::app );
249 if( !f.good() ) {
250 err() << "could not open file \"" << fileName << "\" for read/write";
251 continue;
253 f.seekp( 0, std::ios_base::end );
255 FileEntryHeader header( it->count() );
257 HistoryGroupLocation location;
258 location.fileName = ~localFileName;
259 location.offset = f.tellp();
261 header.write( f );
262 //f.write( (char*)&header, sizeof( FileEntryHeader ) );
264 if( !f.good() ) {
265 err() << "could not write header into file \"" << fileName << "\"";
266 continue;
269 try {
270 ///Store the messages into the archive
271 boost::archive::xml_oarchive arch( f );
272 for( QList<HistoryMessageDesc>::iterator it2 = it->begin(); it2 != it->end(); ++it2 ) {
273 arch << boost::serialization::make_nvp( "message", *it2 );
275 HistoryMessagePointer::Locked l = it2->message;
276 if( l ) {
277 insertions[ l->info().uniqueId() ] = location;
278 } else {
279 err() << "could not lock a message while storing it into the history";
283 catch( std::exception& exc ) {
284 err() << "error while serializing into \"" << fileName << "\":" << exc.what();
288 ///The index maps unique-message-ids to the files they can be found in(including offset to the FileEntryHeader)
289 std::map<Teamwork::UniqueMessageId, HistoryGroupLocation> index;
291 if( !insertions.empty() ) {
292 try {
293 readIndex( index );
295 catch( QString str ) {
296 out( Logger::Warning ) << "error in readIndex:" << str;
299 index.insert( insertions.begin(), insertions.end() );
301 writeIndex( index );
305 m_pending.clear();
307 catch( WaitError err ) {
308 out( Logger::Warning ) << "in MessageHistoryManager::writePending:" << err.str;
310 catch( QString str )
312 err() << "error in MessageHistoryManager::writePending:" << str;
313 m_pending.clear();
316 if( m_lockFile->isLocked() )
317 m_lockFile->unlock();
320 QString MessageHistoryManager::lockFileName() {
321 KUrl file = directory();
322 file.addPath( ".lock" );
323 return file.toLocalFile();
326 void MessageHistoryManager::readIndex( std::map<Teamwork::UniqueMessageId, HistoryGroupLocation>& index ) throw( QString) {
327 KUrl ind = directory();
328 ind.addPath( "index" );
329 if( !ind.isLocalFile() ) throw QString( "the index-file is not local" );
331 std::string indexFile = ~ind.toLocalFile();
334 std::ifstream f( indexFile.c_str(), ios_base::binary );
335 if( !f.good() ) throw QString( "could not open index-file for reading" );
337 try {
338 boost::archive::binary_iarchive arch( f );
339 arch & index;
341 catch( std::exception& exc ) {
342 err() << "error while deserializing the index \"" << indexFile << "\":" << exc.what();
343 throw QString( "error in readIndex" );
348 void MessageHistoryManager::writeIndex( const std::map<Teamwork::UniqueMessageId, HistoryGroupLocation>& index ) throw( QString) {
350 KUrl ind = directory();
351 ind.addPath( "index" );
352 if( !ind.isLocalFile() ) throw QString( "the index-file is not local" );
354 std::string indexFile = ~ind.toLocalFile();
356 std::ofstream f( indexFile.c_str(), ios_base::binary );
357 if( !f.good() ) throw QString( "could not open index-file for writing" );
359 try {
360 ///Store the messages into the archive
361 boost::archive::binary_oarchive arch( f );
362 arch & index;
364 catch( std::exception& exc ) {
365 err() << "error while serializing the index \"" << indexFile << "\":" << exc.what();
366 throw QString( "error in writeIndex" );
371 KUrl MessageHistoryManager::directory() throw(QString)
373 KUrl ul = TeamworkFolderManager::teamworkAbsolute( "messages" );
374 TeamworkFolderManager::createTeamworkFolder();
376 if( !KIO::NetAccess::exists( ul, KIO::NetAccess::SourceSide, 0 ) )
377 KIO::NetAccess::mkdir( ul, 0 );
379 if( !KIO::NetAccess::exists( ul, KIO::NetAccess::SourceSide, 0 ) )
380 throw QString( "could not create messages-directory " ) + ul.toLocalFile();
382 return ul;
385 void MessageHistoryManager::readMessages( const QString& file, uint offset, QList<HistoryMessageDesc>& messages ) throw(QString)
387 KUrl path = directory();
388 path.addPath( file );
390 if( !path.isLocalFile() ) throw QString( "message is not local" );
391 if( !KIO::NetAccess::exists( path, KIO::NetAccess::SourceSide, 0 ) ) throw QString( "index does not exist" );
393 std::ifstream f( path.toLocalFile().toLatin1(), ios_base::binary );
394 f.seekg( offset );
395 if( !f.good() || f.eof() ) throw QString( "could not open file and seek to offset %1 in file %2" ).arg( offset ).arg( path.toLocalFile() );
397 FileEntryHeader header;
399 header.read( f );
400 if( !f.eof() ) {
401 if( !f.good() || f.eof() )
402 throw QString( "could not read a header from file \"%1\"" ).arg( path.toLocalFile() );
404 try {
405 boost::archive::xml_iarchive arch( f );
406 for( int a = 0; a < header.messages; a++ ) {
407 HistoryMessageDesc msg;
408 arch >> boost::serialization::make_nvp( "message", msg );
409 messages << msg;
412 catch( std::exception& exc ) {
413 err() << "error while deserializing from \"" << ~path.toLocalFile() << "\":" << exc.what();
418 HistoryMessagePointer MessageHistoryManager::getMessageFromId( Teamwork::UniqueMessageId id, const KDevTeamworkClientPointer& client ) {
419 std::map<Teamwork::UniqueMessageId, HistoryGroupLocation> index;
420 try {
421 readIndex( index );
422 std::map<Teamwork::UniqueMessageId, HistoryGroupLocation>::iterator it = index.find( id );
423 if( it != index.end() ) {
424 QList<HistoryMessageDesc> messages;
425 readMessages( ~it->second.fileName, it->second.offset, messages );
427 for( QList<HistoryMessageDesc>::iterator it = messages.begin(); it != messages.end(); ++it ) {
428 HistoryMessagePointer::Locked l = it->message;
429 if( l ){
430 if( l->info().uniqueId() == id ) {
431 return fillMessageUser( *it, client );
436 return HistoryMessagePointer();
437 } else {
438 return HistoryMessagePointer();
441 catch( QString str ) {
442 err() << "error in messageFromId:" << ~str;
443 return HistoryMessagePointer();
447 HistoryMessagePointer MessageHistoryManager::fillMessageUser( const HistoryMessageDesc& desc, const KDevTeamworkClientPointer& client ) {
448 HistoryMessagePointer::Locked lmsg = desc.message;
449 KDevTeamworkClientPointer::Locked l = client;
450 if( !l ) {
451 err() << "could not lock KDevTeamworkClient";
452 return HistoryMessagePointer();
454 if( lmsg ) {
455 lmsg->info().setUser( l->getUser( desc.user ) );
456 return lmsg;
457 } else {
458 err() << "could not lock a message in fillMessageUsers";
461 return HistoryMessagePointer();
464 QList<HistoryMessagePointer> MessageHistoryManager::fillMessageUsers( const QList<HistoryMessageDesc>& messages, const KDevTeamworkClientPointer& client ) {
465 QList<HistoryMessagePointer> ret;
466 KDevTeamworkClientPointer::Locked l = client;
468 if( l ) {
469 foreach( const HistoryMessageDesc& desc, messages ) {
470 HistoryMessagePointer::Locked lmsg = desc.message;
471 if( lmsg ) {
472 lmsg->info().setUser( l->getUser( desc.user ) );
473 ret << lmsg;
474 } else {
475 err() << "could not lock a message in fillMessageUsers";
478 } else {
479 err() << "could not lock teamwork-client";
482 return ret;
485 Teamwork::LoggerPrinter MessageHistoryManager::out( Teamwork::Logger::Level level ) {
486 Teamwork::LoggerPrinter ret( m_logger, level );
487 ret << "in MessageHistoryManager: ";
488 return ret;
492 Teamwork::LoggerPrinter MessageHistoryManager::err() {
493 Teamwork::LoggerPrinter ret( m_logger, Teamwork::Logger::Error );
494 ret << "Error in MessageHistoryManager: ";
495 return ret;
498 BOOST_CLASS_IMPLEMENTATION(HistoryMessageDesc, boost::serialization::object_serializable)
500 #include "messagehistorymanager.moc"
502 // kate: space-indent on; indent-width 2; tab-width 2; replace-tabs on