source: indico/indico/MaKaC/common/db.py

Last change on this file was 3bcd5d, checked in by Jose Benito <jose.benito.gonzalez@…>, 2 months ago

[IMP] Update license headers in every file

  • Update to 2013.
  • Closes #1225
  • Property mode set to 100644
File size: 6.9 KB
Line 
1# -*- coding: utf-8 -*-
2##
3##
4## This file is part of Indico.
5## Copyright (C) 2002 - 2013 European Organization for Nuclear Research (CERN).
6##
7## Indico is free software; you can redistribute it and/or
8## modify it under the terms of the GNU General Public License as
9## published by the Free Software Foundation; either version 3 of the
10## License, or (at your option) any later version.
11##
12## Indico is distributed in the hope that it will be useful, but
13## WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15## General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with Indico;if not, see <http://www.gnu.org/licenses/>.
19
20"""This file contains the implementation of some classes dealing with the DB
21    so it's usage is easier and more transparent to the rest of the application
22"""
23import os
24import sys
25import threading
26import pkg_resources
27from contextlib import contextmanager
28
29from ZEO.ClientStorage import ClientStorage
30from ZODB.DB import DB
31import transaction
32import ZODB
33
34from MaKaC.consoleScripts.installBase import getIndicoInstallMode
35skip_imports = getIndicoInstallMode()
36
37
38if not skip_imports:
39    from MaKaC.common.logger import Logger
40
41
42class MaKaCDB(DB):
43    """Subclass of ZODB.DB necessary to remove possible existing dependencies
44        from IC"""
45
46    def classFactory(self, connection, modulename, globalname):
47        if globalname=="PersistentMapping":
48            modulename="persistent.mapping"
49        elif globalname=="PersistentList":
50            modulename="persistent.list"
51        elif modulename.startswith("IndexedCatalog.BTrees."):
52            modulename="BTrees.%s"%modulename[22:]
53        elif modulename.startswith("MaKaC.plugins.EPayment.CERNYellowPay."):
54            modulename="indico.ext.epayment.cern.%s"%modulename[len("MaKaC.plugins.EPayment.CERNYellowPay."):]
55        return DB.classFactory(self, connection, modulename, globalname)
56
57
58class DBMgr:
59    """This class provides the access point to the Shelf (every client will
60        use this class in order to obtain a shelf) and some mechanism to
61        ensure there is only one connection opened to the DB during a single
62        request.
63        This class must not be instantiated, an instance can be obtained
64        though the "getInstance" method (implements the singleton pattern
65        to ensure unicity)
66        Needs to be checked if the class (static) attribute _instance is
67        thread-safe: as it is shared by all the objects of this class,
68        it could provoke concurrency troubles having 2 threads using the db
69        connection at the same time. However, the model under which we are
70        programming is not multi-threaded (mod_python seems to run different
71        interpreters for each apache subprocess, see mod_python doc section
72        4.1) so this thechnique can be used. This has to be taken into account
73        when migrating the system to a multi-threading environment.
74    """
75    _instance = None
76
77    def __init__( self, hostname=None, port=None, max_disconnect_poll=30 ):
78        import Configuration # Please leave this import here, db.py is imported during installation process
79        cfg = Configuration.Config.getInstance()
80
81        if not hostname:
82            hostname = cfg.getDBConnectionParams()[0]
83        if not port:
84            port = cfg.getDBConnectionParams()[1]
85
86        self._storage=ClientStorage((hostname, port), username=cfg.getDBUserName(), password=cfg.getDBPassword(), realm=cfg.getDBRealm(),
87                                    max_disconnect_poll=max_disconnect_poll)
88        self._db=MaKaCDB(self._storage)
89        self._conn = threading.local()
90        self._conn.conn = None
91
92    @classmethod
93    def getInstance( cls, *args, **kwargs ):
94        if cls._instance == None:
95            Logger.get('dbmgr').debug('cls._instance is None')
96            cls._instance=DBMgr(*args, **kwargs)
97        return cls._instance
98
99    @classmethod
100    def setInstance( cls, dbInstance ):
101        cls._instance = dbInstance
102
103    def _getConnObject(self):
104        return self._conn.conn
105
106    def _setConnObject(self, obj):
107        self._conn.conn = obj
108
109
110    def _delConnObject(self):
111        self._conn.conn = None
112
113    def startRequest( self ):
114        """Initialise the DB and starts a new transaction.
115        """
116        self._conn.conn = self._db.open()
117
118    def endRequest( self, commit=True ):
119        """Closes the DB and commits changes.
120        """
121        if commit:
122            self.commit()
123        else:
124            self.abort()
125
126        self._getConnObject().close()
127        self._delConnObject()
128
129    def getDBConnection( self ):
130        return self._getConnObject()
131
132    def isConnected( self ):
133        return hasattr(self._conn, 'conn') and self._conn.conn != None
134
135    def getDBConnCache(self):
136        conn = self._getConnObject()
137        return conn._cache
138
139    def getDBClassFactory(self):
140        return self._db.classFactory
141
142    def commit(self, sub=False):
143        if (sub):
144            transaction.savepoint()
145        else:
146            transaction.commit()
147
148    def commitZODBOld(self, sub=False):
149        transaction.commit(sub)
150
151    def abort(self):
152        transaction.abort()
153
154    def sync(self):
155        self._getConnObject().sync()
156
157    def pack( self, days=1 ):
158        self._storage.pack(days=days)
159
160    def undoInfo(self, stepNumber=0):
161        # One step is made of 1000 transactions. First step is 0 and returns
162        # transactions 0 to 999.
163        return self._db.undoInfo(stepNumber*1000, (stepNumber+1)*1000)
164
165    def undo(self, trans_id):
166        self._db.undo(trans_id)
167
168    def getDBSize( self ):
169        """Return an approximate size of the database, in bytes."""
170        return self._storage.getSize()
171
172    def loadObject(self, oid, version):
173        return self._storage.load(oid, version)
174
175    def storeObject(self, oid, serial, data, version, trans):
176        return self._storage.store(oid, serial, data, version, trans)
177
178    def tpcBegin(self, trans):
179        self._storage.tpc_begin(trans)
180
181    def tpcVote(self, trans):
182        self._storage.tpc_vote(trans)
183
184    def tpcFinish(self, trans):
185        self._storage.tpc_finish(trans)
186
187    @contextmanager
188    def transaction(self, sync=False):
189        """
190        context manager (`with`)
191        """
192        if sync:
193            self.sync()
194        yield self.getDBConnection()
195        self.commit()
196
197    # ZODB version check
198    try:
199        zodbPkg = pkg_resources.require('ZODB3')[0]
200        zodbVersion = zodbPkg.parsed_version
201        zodbVersion = (int(zodbVersion[0]), int(zodbVersion[1]))
202    except pkg_resources.DistributionNotFound:
203        # Very old versions, in which ZODB didn't register
204        # with pkg_resources
205        import ZODB
206        zodbVersion = ZODB.__version__.split('.')
207
208    if int(zodbVersion[0]) < 3:
209        raise Exception("ZODB 3 required! %s found" % zodbPkg.version)
210    elif int(zodbVersion[1]) < 7:
211        commit = commitZODBOld
Note: See TracBrowser for help on using the repository browser.