1 /* This file is part of the KDE Project
2 Copyright (c) 2008 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 "servicemanager.h"
20 #include "servicecontroller.h"
22 #include <QtCore/QHash>
23 #include <QtDBus/QtDBus>
26 #include <KServiceTypeTrader>
30 //extern QDBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage&));
32 Nepomuk::ServiceManager
* Nepomuk::ServiceManager::s_self
= 0;
35 class DependencyTree
: public QHash
<QString
, QStringList
>
39 * Cleanup the tree. Remove all services
40 * that have an invalid dependency branch
45 * \return true if service depends on dependency
47 bool dependsOn( const QString
& service
, const QString
& dependency
);
50 * Remove the service and all those depending on it
51 * from the services list
53 void removeService( const QString
& service
);
55 QStringList
servicesDependingOn( const QString
& service
);
58 void DependencyTree::cleanup()
60 // cleanup dependency tree
61 QHash
<QString
, QStringList
> tmpTree( *this );
62 for( QHash
<QString
, QStringList
>::const_iterator it
= tmpTree
.constBegin();
63 it
!= tmpTree
.constEnd(); ++it
) {
64 QString service
= it
.key();
65 QStringList dependencies
= it
.value();
67 foreach( const QString
&dep
, dependencies
) {
68 // check if the service depends on a non-existing service
69 if( !contains( dep
) ) {
70 kDebug() << "Found invalid dependency:" << service
<< "depends on non-existing service" << dep
;
71 // ignore the service and all those depending on it
72 removeService( service
);
76 // check if the service itself is a dependency of any of its
78 else if( dependsOn( dep
, service
) ) {
79 kDebug() << "Found dependency loop:" << service
<< "depends on" << dep
<< "and vice versa";
80 // ignore the service and all those depending on it
81 removeService( service
);
88 bool DependencyTree::dependsOn( const QString
& service
, const QString
& dependency
)
90 foreach( const QString
&dep
, value( service
) ) {
91 if( dep
== dependency
||
92 dependsOn( dep
, dependency
) ) {
100 void DependencyTree::removeService( const QString
& service
)
102 if( contains( service
) ) {
105 // remove any service depending on the removed one
106 QHash
<QString
, QStringList
> tmpTree( *this );
107 for( QHash
<QString
, QStringList
>::const_iterator it
= tmpTree
.constBegin();
108 it
!= tmpTree
.constEnd(); ++it
) {
109 if( it
.value().contains( service
) ) {
110 removeService( it
.key() );
116 QStringList
DependencyTree::servicesDependingOn( const QString
& service
)
119 for( QHash
<QString
, QStringList
>::const_iterator it
= constBegin();
120 it
!= constEnd(); ++it
) {
121 if( it
.value().contains( service
) ) {
122 sl
.append( it
.key() );
130 class Nepomuk::ServiceManager::Private
133 Private( ServiceManager
* p
)
134 : m_initialized(false),
138 // map of all services, started and stopped ones
139 QHash
<QString
, ServiceController
*> services
;
141 // clean dependency tree
142 DependencyTree dependencyTree
;
144 // services that wait for dependencies to initialize
145 QSet
<ServiceController
*> pendingServices
;
147 ServiceController
* findService( const QString
& name
);
148 void buildServiceMap();
151 * Try to start a service including all its
152 * dependecies. Async.
154 void startService( ServiceController
* );
157 * Stop a service and all services depending on it.
159 void stopService( ServiceController
* );
162 * Start pending services based on the newly initialized service newService
164 void startPendingServices( ServiceController
* newService
);
167 * Slot connected to all ServiceController::serviceInitialized
170 void _k_serviceInitialized( ServiceController
* );
178 void Nepomuk::ServiceManager::Private::buildServiceMap()
180 if( !m_initialized
) {
181 const KService::List modules
= KServiceTypeTrader::self()->query( "NepomukService" );
182 for( KService::List::ConstIterator it
= modules
.constBegin(); it
!= modules
.constEnd(); ++it
) {
183 KService::Ptr service
= *it
;
184 QStringList deps
= service
->property( "X-KDE-Nepomuk-dependencies", QVariant::StringList
).toStringList();
185 if ( deps
.isEmpty() ) {
186 deps
.append( "nepomukstorage" );
188 deps
.removeAll( service
->desktopEntryName() );
189 dependencyTree
.insert( service
->desktopEntryName(), deps
);
192 dependencyTree
.cleanup();
194 for( KService::List::ConstIterator it
= modules
.constBegin(); it
!= modules
.constEnd(); ++it
) {
195 KService::Ptr service
= *it
;
196 if( dependencyTree
.contains( service
->desktopEntryName() ) ) {
197 ServiceController
* sc
= new ServiceController( service
, q
);
198 connect( sc
, SIGNAL(serviceInitialized(ServiceController
*)),
199 q
, SLOT(_k_serviceInitialized(ServiceController
*)) );
200 services
.insert( sc
->name(), sc
);
204 m_initialized
= true;
209 Nepomuk::ServiceController
* Nepomuk::ServiceManager::Private::findService( const QString
& name
)
211 QHash
<QString
, ServiceController
*>::iterator it
= services
.find( name
);
212 if( it
!= services
.end() ) {
219 void Nepomuk::ServiceManager::Private::startService( ServiceController
* sc
)
221 kDebug( 300002 ) << sc
->name();
223 if( !sc
->isRunning() ) {
224 // start dependencies if possible
225 bool needToQueue
= false;
226 foreach( const QString
&dependency
, dependencyTree
[sc
->name()] ) {
227 ServiceController
* depSc
= findService( dependency
);
228 if ( !depSc
->isInitialized() ) {
229 kDebug() << "Queueing" << sc
->name() << "due to dependency" << dependency
;
230 pendingServices
.insert( sc
);
234 if ( !depSc
->isRunning() ) {
235 startService( depSc
);
240 if ( !needToQueue
) {
247 void Nepomuk::ServiceManager::Private::stopService( ServiceController
* sc
)
249 if( sc
->isRunning() ) {
250 // shut down any service depending of this one first
251 foreach( const QString
&dep
, dependencyTree
.servicesDependingOn( sc
->name() ) ) {
252 stopService( services
[dep
] );
260 void Nepomuk::ServiceManager::Private::startPendingServices( ServiceController
* newService
)
262 // check the list of pending services and start as many as possible
263 // (we can start services whose dependencies are initialized)
264 QList
<ServiceController
*> sl
= pendingServices
.toList();
265 foreach( ServiceController
* service
, sl
) {
266 if ( service
->dependencies().contains( newService
->name() ) ) {
267 // try to start the service again
268 pendingServices
.remove( service
);
269 startService( service
);
275 void Nepomuk::ServiceManager::Private::_k_serviceInitialized( ServiceController
* sc
)
277 kDebug() << "Service initialized:" << sc
->name();
278 if ( !pendingServices
.isEmpty() ) {
279 startPendingServices( sc
);
281 emit q
->serviceInitialized( sc
->name() );
286 Nepomuk::ServiceManager::ServiceManager( QObject
* parent
)
291 // qDBusAddSpyHook(messageFilter);
295 Nepomuk::ServiceManager::~ServiceManager()
302 void Nepomuk::ServiceManager::startAllServices()
304 d
->buildServiceMap();
306 for( QHash
<QString
, ServiceController
*>::iterator it
= d
->services
.begin();
307 it
!= d
->services
.end(); ++it
) {
308 ServiceController
* serviceControl
= it
.value();
310 if( serviceControl
->autostart() ) {
311 d
->startService( serviceControl
);
317 void Nepomuk::ServiceManager::stopAllServices()
319 for( QHash
<QString
, ServiceController
*>::iterator it
= d
->services
.begin();
320 it
!= d
->services
.end(); ++it
) {
321 ServiceController
* serviceControl
= it
.value();
322 d
->stopService( serviceControl
);
327 bool Nepomuk::ServiceManager::startService( const QString
& name
)
329 if( ServiceController
* sc
= d
->findService( name
) ) {
330 if( !sc
->isRunning() ) {
331 // start dependencies if possible
332 foreach( const QString
&dependency
, d
->dependencyTree
[name
] ) {
333 if ( ServiceController
* depSc
= d
->findService( dependency
) ) {
334 if( depSc
->autostart() || depSc
->startOnDemand() ) {
335 if ( !startService( dependency
) ) {
336 kDebug() << "Cannot start dependency" << dependency
;
341 kDebug() << "Dependency" << dependency
<< "can not be started automatically. It is not an autostart or start on demand service";
346 kDebug() << "Invalid dependency:" << dependency
;
352 return sc
->waitForInitialized();
355 // failed to start service
360 // service already running
365 // could not find service
371 bool Nepomuk::ServiceManager::stopService( const QString
& name
)
373 if( ServiceController
* sc
= d
->findService( name
) ) {
374 if( sc
->isRunning() ) {
375 d
->stopService( sc
);
384 QStringList
Nepomuk::ServiceManager::runningServices() const
387 for( QHash
<QString
, ServiceController
*>::iterator it
= d
->services
.begin();
388 it
!= d
->services
.end(); ++it
) {
389 ServiceController
* serviceControl
= it
.value();
390 if( serviceControl
->isRunning() ) {
391 sl
.append( serviceControl
->name() );
398 QStringList
Nepomuk::ServiceManager::availableServices() const
400 return d
->services
.keys();
404 bool Nepomuk::ServiceManager::isServiceInitialized( const QString
& service
) const
406 if ( ServiceController
* sc
= d
->findService( service
) ) {
407 return sc
->isInitialized();
415 bool Nepomuk::ServiceManager::isServiceAutostarted( const QString
& name
)
417 if ( ServiceController
* sc
= d
->findService( name
) ) {
418 return sc
->autostart();
426 void Nepomuk::ServiceManager::setServiceAutostarted( const QString
& name
, bool autostart
)
428 if ( ServiceController
* sc
= d
->findService( name
) ) {
429 sc
->setAutostart( autostart
);
434 //#define MODULES_PATH "/modules/"
436 // FIXME: Use our own system and dbus path
437 // on-demand module loading
438 // this function is called by the D-Bus message processing function before
439 // calls are delivered to objects
440 // void Nepomuk::ServiceManager::messageFilter( const QDBusMessage& message )
442 // if ( message.type() != QDBusMessage::MethodCallMessage )
445 // QString obj = message.path();
446 // if ( !obj.startsWith( MODULES_PATH ) )
449 // QString name = obj.mid( strlen( MODULES_PATH ) );
450 // if ( name == "ksycoca" )
451 // return; // Ignore this one.
453 // if( ServiceController* sc = self()->d->findService( name ) ) {
454 // if( sc->startOnDemand() ) {
455 // self()->d->startService( sc );
456 // sc->waitForInitialized();
461 #include "servicemanager.moc"