This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
expression/src/modules/dbxmldatabases.py

208 lines
8.0 KiB
Python

# -*- coding: UTF-8 -*-
# Expression
# By: Frederic Peters <fpeters@entrouvert.com>
# Emmanuel Raviart <eraviart@entrouvert.com>
# Sébastien Ducoulombier <sebastien.ducoulombier@lesdeveloppementsdurables.com>
#
# Copyright (C) 2004 Entr'ouvert, Frederic Peters & Emmanuel Raviart
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""Berkeley DB XML Database Manager Module"""
import os.path
import shutil
import sys
import bsddb3.db as db
import dbxml
import libxml2
import expression.core.elements as elements
import expression.core.environs as environs
import expression.core.dataholders as dataholders
import expression.core.html as html
import expression.core.logs as logs
import expression.core.namespaces as namespaces
import expression.core.things as things
import expression.modules.dbxmlcontainers as dbxmlcontainers
class DbXmlDatabase(dataholders.DataHolder):
""" A directory-like station that maps to the database environment manager. """
_environment = None
_manager = None
_containers = None
isUriDirectory = True
def generateXml(self, layout):
""" Just generates a page that confirms this station is the database station. """
layout.append(
html.html(
html.body(
html.p(
"Berkeley DB XML database at %s" % DbXmlDatabase._manager.getHome()
)
)
)
)
return True
def walkToItem(self, uriPathFragments, command = None, instruction = None):
""" Returns the next station if the uriPathFragment is a container name. """
name = uriPathFragments[0]
if name in DbXmlDatabase._containers:
item = dbxmlcontainers.DbXmlContainerHolder(
DbXmlDatabase._containers[name],
pathFragment = name,
previous = self,
parent = self,
uriPathFragment = name,
)
configuration = environs.getVar("configuration")
pythonClass = configuration.getConfigString(
"""yep:module[@name="%s"]/yep:container[@name="%s"]/@pythonClass""" % (__init__.__module__, name),
None
)
if pythonClass:
item.__class__ = configuration.convertStringToPythonClass(pythonClass, )
return item, uriPathFragments[1:]
return super(DbXmlDatabase, self).walkToItem(uriPathFragments, command, instruction)
class XQuery(things.Thing):
""" Element xquery contains an XQuery expression to be evaluated.
"""
def generateXml(self, layout):
""" Appends "layout" with the result of this query
"""
manager = DbXmlDatabase._manager
queryContext = manager.createQueryContext()
try:
results = manager.query(self.node.content, queryContext)
except RuntimeError, e:
logs.error("Error in query: %s" % e)
layout.append(html.p(_("Error in query: %s") % e))
else:
output = "\n".join([value.asString() for value in results])
try:
doc = libxml2.readDoc(output, None, None, 0)
except libxml2.treeError:
# output is not an XML document, considering it as plain text
layout.append(html.html(html.body(html.pre(output))))
else:
layout.append(doc.getRootElement())
return True
elements.registerElement(namespaces.yep.uri, "xquery", XQuery)
def __init__():
""" Opens a Berkeley DB environment and an XmlManager around it.
Then opens all configured containers.
"""
DbXmlDatabase._environment = db.DBEnv()
DbXmlDatabase._environment.set_shm_key(35)
configuration = environs.getVar("configuration")
dbHome = configuration.getConfigAbsolutePath(
"""yep:module[@name="%s"]/yep:environment/@dbHome""" % __init__.__module__,
"/var/lib/expression/db"
)
DbXmlDatabase._environment.open(
dbHome, 0
|db.DB_INIT_LOCK
|db.DB_INIT_LOG
|db.DB_INIT_MPOOL
|db.DB_INIT_REP
|db.DB_INIT_TXN
|db.DB_CREATE
|db.DB_SYSTEM_MEM
|db.DB_THREAD
,
0
)
manager = dbxml.XmlManager(DbXmlDatabase._environment, dbxml.DBXML_ADOPT_DBENV)
DbXmlDatabase._manager = manager
DbXmlDatabase._containers = {}
updateContext = manager.createUpdateContext()
for containerName in configuration.getConfigList("""yep:module[@name="%s"]/yep:container/@name""" % __init__.__module__):
try:
container = manager.openContainer(
containerName, 0
|dbxml.DBXML_CHKSUM
)
except RuntimeError, e:
logs.warning("Container %s could not be open: %s" % (containerName, e))
filePath = os.path.join(dbHome, containerName)
if os.path.exists(filePath):
logs.debug("File %s exists. Attempting to upgrade it.", filePath)
backupPath = filePath + "_backup"
shutil.copyfile(filePath, backupPath)
try:
manager.upgradeContainer(containerName, updateContext)
except RuntimeError, e:
logs.error("Container %s could not be upgraded: %s" % (containerName, e))
shutil.move(backupPath, filePath)
__del__()
raise
else:
logs.debug("File %s was succesfully upgraded.", filePath)
os.unlink(backupPath)
else:
try:
logs.debug("File %s does not exist. Creating it.", filePath)
container = manager.createContainer(
containerName, 0
|dbxml.DBXML_CHKSUM
)
except RuntimeError, e:
logs.error("Could not create container %s: %s" % (containerName, e))
__del__()
raise
populationPath = configuration.getConfigAbsolutePath(
"""yep:module[@name="%s"]/yep:population/@path""" % __init__.__module__,
)
if populationPath:
populationDoc = libxml2.readFile(populationPath, "utf-8", 0)
setNode = populationDoc.getRootElement()
for documentNode in setNode.xpathEval("container[@name='%s']/*" % containerName):
for node in documentNode.xpathEval("*"):
doc = libxml2.newDoc("1.0")
doc.setRootElement(node)
container.putDocument(documentNode.prop("name"), doc.serialize(), updateContext)
indexSpecification = dbxml.XmlIndexSpecification()
for node in configuration.evaluateXpath(
"""yep:module[@name="%s"]/yep:container[@name="%s"]/yep:index""" % (
__init__.__module__,
containerName,
)
):
uri, name, index = node.prop("uri"), node.prop("name"), node.prop("index")
indexSpecification.addIndex(uri, name, index)
container.setIndexSpecification(indexSpecification, updateContext)
DbXmlDatabase._containers[containerName] = container
def __del__():
""" Closes containers and the database environment.
"""
del DbXmlDatabase._containers
DbXmlDatabase._environment.close()