configuration/include/resourcesuser.h

00001 /********************************************************************************
00002  *  FARSA Experiments Library                                                   *
00003  *  Copyright (C) 2007-2012                                                     *
00004  *  Gianluca Massera <emmegian@yahoo.it>                                        *
00005  *  Stefano Nolfi <stefano.nolfi@istc.cnr.it>                                   *
00006  *  Onofrio Gigliotta <onofrio.gigliotta@istc.cnr.it>                           *
00007  *  Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it>                         *
00008  *                                                                              *
00009  *  This program is free software; you can redistribute it and/or modify        *
00010  *  it under the terms of the GNU General Public License as published by        *
00011  *  the Free Software Foundation; either version 2 of the License, or           *
00012  *  (at your option) any later version.                                         *
00013  *                                                                              *
00014  *  This program is distributed in the hope that it will be useful,             *
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00017  *  GNU General Public License for more details.                                *
00018  *                                                                              *
00019  *  You should have received a copy of the GNU General Public License           *
00020  *  along with this program; if not, write to the Free Software                 *
00021  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00022  ********************************************************************************/
00023 
00024 #ifndef RESOURCESUSER_H
00025 #define RESOURCESUSER_H
00026 
00027 #include "parametersettable.h"
00028 #include "configurationconfig.h"
00029 #include "configurationexceptions.h"
00030 #include "resource.h"
00031 #include <QMap>
00032 #include <QString>
00033 #include <QStringList>
00034 #include <QMutexLocker>
00035 #include <QSet>
00036 
00037 class QObject;
00038 
00039 namespace farsa {
00040 
00041 class ResourcesUser;
00042 class SimpleResourcesUser;
00043 class ConcurrentResourcesUser;
00044 class ResourcesLocker;
00045 
00070 class FARSA_CONF_API ResourcesUser : public  Resource
00071 {
00072 public:
00084     virtual void shareResourcesWith(ResourcesUser* other);
00085 
00086 protected:
00092     ResourcesUser();
00093 
00102     ResourcesUser(const ResourcesUser& other) throw();
00103 
00112     ResourcesUser& operator=(const ResourcesUser& other) throw();
00113 
00119     virtual ~ResourcesUser();
00120 
00130     virtual void notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType) = 0;
00131 
00135     ResourceCollectionHolder m_resources;
00136 
00141     friend class ResourceHandler;
00142 
00147     friend class ConcurrentResourcesUser;
00148 };
00149 
00219 class FARSA_CONF_API SimpleResourcesUser : public ResourcesUser
00220 {
00221 public:
00225     SimpleResourcesUser();
00226 
00235     SimpleResourcesUser(const SimpleResourcesUser& other);
00236 
00245     SimpleResourcesUser& operator=(const SimpleResourcesUser& other);
00246 
00250     virtual ~SimpleResourcesUser();
00251 
00263     virtual void shareResourcesWith(ResourcesUser* other);
00264 
00272     template<class T>
00273     void declareResource(QString name, T* resource)
00274     {
00275         // Now we have to get the resource (creating it if necessary). 
00276         ResourceHandler* h = m_resources->getResource(name, true);
00277 
00278         // Now setting the resource. If this throws an exception we could have simply created a non-existing
00279         // resource above that doesn't modify the external behaviour of this and other classes (so we can say
00280         // that this function is strongly exception-safe)
00281         h->set(resource);
00282     }
00283 
00293     template<class T>
00294     T* getResource(QString name)
00295     {
00296         // Trying to retrieve the resource
00297         ResourceHandler* h = m_resources->getResource(name, false);
00298 
00299         if ((h == NULL) || (!h->exists())){
00300             throw ResourceNotDeclaredException(name.toAscii().data());
00301         }
00302 
00303         T* resource = h->get<T>();
00304         if (resource == NULL) {
00305             throw ResourceTypeMismatchException(name.toAscii().data(), typeid(T).name());
00306         }
00307 
00308         return resource;
00309     }
00310 
00317     void deleteResource(QString name);
00318 
00326     bool hasResource(QString name) const
00327     {
00328         return m_resources->hasResource(name);
00329     }
00330 
00331 protected:
00340     virtual void resourceChanged(QString name, ResourceChangeType changeType);
00341 
00354     template<class T>
00355     T* getResource()
00356     {
00357         if (!m_beingNotified) {
00358             return NULL;
00359         }
00360 
00361         if (!m_notifiedResourceHandler->exists()) {
00362             // The resource could have been deleted
00363             throw ResourceNotDeclaredException(m_notifiedResourceHandler->name().toAscii().data());
00364         }
00365         T* resource = m_notifiedResourceHandler->get<T>();
00366         if (resource == NULL) {
00367             throw ResourceTypeMismatchException(m_notifiedResourceHandler->name().toAscii().data(), typeid(T).name());
00368         }
00369 
00370         return resource;
00371     }
00372 
00373 private:
00383     virtual void notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType);
00384 
00389     void removeAllNotifications();
00390 
00394     void forceBeingNotified();
00395 
00402     bool m_beingNotified;
00403 
00410     ResourceHandler* m_notifiedResourceHandler;
00411 
00415     QSet<ResourceHandler*> m_observedResources;
00416 };
00417 
00581 class FARSA_CONF_API ConcurrentResourcesUser : public ResourcesUser
00582 {
00583 public:
00587     ConcurrentResourcesUser();
00588 
00592     virtual ~ConcurrentResourcesUser();
00593 
00604     virtual void shareResourcesWith(ResourcesUser* buddy);
00605 
00620     void usableResources(QStringList resources);
00621 
00635     void addUsableResource(QString resource);
00636 
00652     void addUsableResources(QStringList resources);
00653 
00664     void removeUsableResource(QString resource);
00665 
00676     void removeUsableResources(QStringList resources);
00677 
00685     void removeAllUsableResources();
00686 
00715     template<class T>
00716     void declareResource(QString name, T* resource, QString lockBuddy = "")
00717     {
00718         if ((m_lockAcquired > 0) || m_beingNotified) {
00719             throw WrongResourceLockStatusForOperation("declareResource", true);
00720         }
00721 
00722         // Getting the lock on the resource collection
00723         QMutexLocker collectionLocker(&(m_resources->getLock()));
00724 
00725         // Now we have to get the resource (creating it if necessary) and then acquire the lock on the resource
00726         ResourceHandler* h = m_resources->getResource(name, true);
00727         if (!lockBuddy.isEmpty()) {
00728             ResourceHandler* lockBuddyHandler = m_resources->getResource(lockBuddy, false);
00729             if ((lockBuddyHandler == NULL) || (!lockBuddyHandler->exists())) {
00730                 throw ResourceNotDeclaredException(lockBuddy.toAscii().data());
00731             }
00732             h->shareLockWith(lockBuddyHandler);
00733         }
00734         ResourcesMutexesLocker resourceLocker(this, h);
00735 
00736         // Before setting the resource, we can release the lock on Resource Manager
00737         collectionLocker.unlock();
00738 
00739         // Now setting the resource (the lock on the resource has been acquired here and will be released after
00740         // returning from the following call)
00741         h->set(resource);
00742     }
00743 
00765     template<class T>
00766     T* getResource(QString name, bool* resourceExists = NULL)
00767     {
00768         if ((m_lockAcquired == 0) && ((!m_beingNotified) || (m_notifiedResourceHandler->name() != name))) {
00769             throw WrongResourceLockStatusForOperation("getResource", false);
00770         }
00771 
00772         if (!m_usableResources.contains(name)) {
00773             throw ResourceNotUsableException(name.toAscii().data());
00774         }
00775 
00776         // Getting the resource. It could be non-existent, we have to check again
00777         if (resourceExists != NULL) {
00778             // This will be set to false after the check
00779             *resourceExists = true;
00780         }
00781         ResourceHandler* h = m_usableResources[name];
00782         if (!h->exists()) {
00783             if (resourceExists != NULL) {
00784                 *resourceExists = false;
00785             } else {
00786                 throw ResourceNotDeclaredException(name.toAscii().data());
00787             }
00788         }
00789 
00790         T* resource = h->get<T>();
00791         if (resource == NULL) {
00792             throw ResourceTypeMismatchException(name.toAscii().data(), typeid(T).name());
00793         }
00794 
00795         return resource;
00796     }
00797 
00806     void deleteResource(QString name);
00807 
00818     bool hasResource(QString name) const;
00819 
00828     bool usedResourcesExist(QStringList* nonExistingResources = NULL) const;
00829 
00830 protected:
00846     virtual void resourceChanged(QString name, ResourceChangeType changeType);
00847 
00859     template<class T>
00860     T* getResource()
00861     {
00862         if (!m_beingNotified) {
00863             if (m_lockAcquired > 0) {
00864                 return NULL;
00865             } else {
00866                 throw WrongResourceLockStatusForOperation("getResource (inside resourceChanged())", false);
00867             }
00868         }
00869 
00870         if (!m_notifiedResourceHandler->exists()) {
00871             // The resource could have been deleted
00872             throw ResourceNotDeclaredException(m_notifiedResourceHandler->name().toAscii().data());
00873         }
00874         T* resource = m_notifiedResourceHandler->get<T>();
00875         if (resource == NULL) {
00876             throw ResourceTypeMismatchException(m_notifiedResourceHandler->name().toAscii().data(), typeid(T).name());
00877         }
00878 
00879         return resource;
00880     }
00881 
00882 private:
00889     void lockAll();
00890 
00897     void unlockAll();
00898 
00908     virtual void notifyResourceChange(ResourceHandler* resource, ResourceChangeType changeType);
00909 
00914     void removeAllNotifications();
00915 
00919     void forceBeingNotified();
00920 
00929     void lockResources(const QList<ResourceHandler*>& resources);
00930 
00939     void unlockResources(const QList<ResourceHandler*>& resources);
00940 
00950     class ResourcesMutexesLocker
00951     {
00952     public:
00960         ResourcesMutexesLocker(ConcurrentResourcesUser *resourcesUser, const QList<ResourceHandler *>& resources) :
00961             m_resourcesUser(resourcesUser),
00962             m_resources(resources)
00963         {
00964             m_resourcesUser->lockResources(m_resources);
00965         }
00966 
00974         ResourcesMutexesLocker(ConcurrentResourcesUser *resourcesUser, ResourceHandler* resource) :
00975             m_resourcesUser(resourcesUser),
00976             m_resources(QList<ResourceHandler *>() << resource)
00977         {
00978             m_resourcesUser->lockResources(m_resources);
00979         }
00980 
00987         ~ResourcesMutexesLocker()
00988         {
00989             m_resourcesUser->unlockResources(m_resources);
00990         }
00991 
00992     private:
00996         ConcurrentResourcesUser *const m_resourcesUser;
00997 
01001         QList<ResourceHandler *> m_resources;
01002     };
01003 
01011     unsigned int m_lockAcquired;
01012 
01019     bool m_beingNotified;
01020 
01027     ResourceHandler* m_notifiedResourceHandler;
01028 
01032     QMap<QString, ResourceHandler*> m_usableResources;
01033 
01037     ConcurrentResourcesUser(const ConcurrentResourcesUser& other);
01038 
01042     ConcurrentResourcesUser& operator=(const ConcurrentResourcesUser& other);
01043 
01048     friend class ResourcesLocker;
01049 };
01050 
01083 class FARSA_CONF_API ResourcesLocker
01084 {
01085 public:
01095     ResourcesLocker(ConcurrentResourcesUser *resourcesUser, bool acquireLock = true);
01096 
01102     ~ResourcesLocker();
01103 
01109     void lock();
01110 
01116     void unlock();
01117 
01118 private:
01122     ConcurrentResourcesUser *const m_resourcesUser;
01123 
01158     bool m_lockedByUs;
01159 };
01160 
01161 } // end namespace farsa
01162 
01163 #endif