diff --git a/plugins/mongodb_datasource/confspec.py b/plugins/mongodb_datasource/confspec.py index b3bbdd7..79bbcc0 100644 --- a/plugins/mongodb_datasource/confspec.py +++ b/plugins/mongodb_datasource/confspec.py @@ -4,10 +4,11 @@ from lodel.settings.validator import SettingValidator CONFSPEC = { 'lodel2.datasource.mongodb_datasource.*':{ + 'read_only': (True, SettingValidator('bool')), 'host': ('localhost', SettingValidator('host')), 'port': (None, SettingValidator('string')), 'db_name':('lodel', SettingValidator('string')), 'username': (None, SettingValidator('string')), 'password': (None, SettingValidator('string')) } -} \ No newline at end of file +} diff --git a/plugins/mongodb_datasource/main.py b/plugins/mongodb_datasource/main.py index 32765bc..8bcba04 100644 --- a/plugins/mongodb_datasource/main.py +++ b/plugins/mongodb_datasource/main.py @@ -11,13 +11,23 @@ import urllib from lodel import logger -from .utils import mongodbconnect, object_collection_name, MONGODB_SORT_OPERATORS_MAP +from .utils import mongodbconnect, object_collection_name, \ + connect, MONGODB_SORT_OPERATORS_MAP class MongoDbDataSourceError(Exception): pass class MongoDbDatasource(object): + ##@brief Stores existing connections + # + #The key of this dict is a hash of the connection string + ro parameter. + #The value is a dict with 2 keys : + # - conn_count : the number of instanciated datasource that use this + #connection + # - db : the pymongo database object instance + _connections = dict() + ##@brief Mapping from lodel2 operators to mongodb operator lodel2mongo_op_map = { '=':'$eq', '<=':'$lte', '>=':'$gte', '!=':'$ne', '<':'$lt', @@ -26,10 +36,34 @@ class MongoDbDatasource(object): mongo_op_re = ['$in', '$nin'] wildcard_re = re.compile('[^\\\\]\*') - ## @brief instanciates a database object given a connection name - # @param connection_name str - def __init__(self, connection_name): - self.r_database = mongodbconnect(connection_name) + ##@brief instanciates a database object given a connection name + #@param host str : hostname or IP + #@param port int : mongodb listening port + #@param db_name str + #@param username str + #@param password str + #@param ro bool : If True the Datasource is for read only, else the + #Datasource is write only ! + def __init__(self, host, port, db_name, username, password, read_only = False): + ##@brief Connections infos that can be kept securly + self.__db_infos = {'host': host, 'port': port, 'db_name': db_name} + ##@brief Is the instance read only ? (if not it's write only) + self.__read_only = bool(read_only) + ##@brief Uniq ID for mongodb connection + self.__conn_hash= None + ##@brief Stores the connection to MongoDB + self.database = self.__connect(username, password) + + ##@brief Destructor that attempt to close connection to DB + # + #Decrease the conn_count of associated MongoDbDatasource::_connections + #item. If it reach 0 close the connection to the db + #@see MongoDbDatasource::__connect() + def __del__(self): + self._connections[self.__conn_hash]['conn_count'] -= 1 + if self._connections[self.__conn_hash]['conn_count'] <= 0: + self._connections[self.__conn_hash]['db'].close() + del(self._connections[self.__conn_hash]) ##@brief returns a selection of documents from the datasource #@param target Emclass @@ -131,6 +165,24 @@ class MongoDbDatasource(object): res = self.__collection.insert_many(datas_list) return list(result.inserted_ids) + ##@brief Connect to database + #@not this method avoid opening two times the same connection using + #MongoDbDatasource::_connections static attribute + #@param host str : hostname or IP + #@param port int : mongodb listening port + #@param db_name str + #@param username str + #@param password str + #@param ro bool : If True the Datasource is for read only, else the + def __connect(self, username, password, ro): + conn_string = connection_string( + username = username, password = password, **self.__db_infos) + conn_string += "__ReadOnly__:"+self.__read_only + self.__conf_hash = conn_h = hash(conn_string) + if conn_h in self._connections: + self._connections[conn_h]['conn_count'] += 1 + return self._connections[conn_h]['db'] + ##@brief Return a pymongo collection given a LeObject child class #@param leobject LeObject child class (no instance) #return a pymongo.collection instance diff --git a/plugins/mongodb_datasource/utils.py b/plugins/mongodb_datasource/utils.py index 2574981..9320326 100644 --- a/plugins/mongodb_datasource/utils.py +++ b/plugins/mongodb_datasource/utils.py @@ -46,12 +46,19 @@ def get_connection_args(connnection_name='default'): # @return MongoClient def mongodbconnect(connection_name): login, password, host, port, dbname = get_connection_args(connection_name) - connection_string = 'mongodb://%s:%s@%s:%s' % (login, password, host, port) - connection = MongoClient(connection_string) + return connect(host, port, db_name, username, password) + +def connection_string(host, port, db_name, username, password): + return 'mongodb://%s:%s@%s:%s' % (login, password, host, port) + +def connect(host, port, db_name, username, password): + connection = MongoClient( + connection_string(host, port, db_name, username, password)) database = connection[dbname] return database + ## @brief Returns a collection name given a EmClass # @param class_object EmClass # @return str