269 lines
10 KiB
Python
269 lines
10 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.faults as faults
|
|
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.
|
|
"""
|
|
_results = None
|
|
_errorMessage = None
|
|
|
|
def __init__(self, *a, **b):
|
|
super(XQuery, self).__init__(*a, **b)
|
|
self._results = None
|
|
self._errorMessage = None
|
|
|
|
def getResults(self):
|
|
""" Evaluates the content of this element as an XQuery expression
|
|
and returns results.
|
|
"""
|
|
if self._results is None:
|
|
manager = DbXmlDatabase._manager
|
|
queryContext = manager.createQueryContext()
|
|
for ns in namespaces.nsList:
|
|
queryContext.setNamespace(ns.name, ns.uri)
|
|
childNodes = self.evaluateXpath("*")
|
|
query = childNodes[0].serialize()
|
|
try:
|
|
self._results = manager.query(query, queryContext)
|
|
except RuntimeError, e:
|
|
logs.error("XQuery failed: %s" % e)
|
|
logs.error("Query was: %s" % query)
|
|
self._errorMessage = e
|
|
return self._results
|
|
|
|
def generateXml(self, layout):
|
|
""" Appends "layout" with the result of this query
|
|
"""
|
|
# output is not an XML document, let's try it as a list of docs.
|
|
result = self.getResults()
|
|
if result is None:
|
|
return False
|
|
results.reset()
|
|
try:
|
|
div = html.div()
|
|
for value in results:
|
|
doc = libxml2.readDoc(value.asString(), None, None, 0)
|
|
element = self.newElement(doc.getRootElement())
|
|
element.generateXml(div)
|
|
layout.append(div)
|
|
except libxml2.treeError:
|
|
# values are not whole XML docs. Let's put them in <p>.
|
|
results.reset()
|
|
div = html.div()
|
|
for value in results:
|
|
div.append(html.p(value.asString()))
|
|
layout.append(div)
|
|
|
|
def styled(self):
|
|
""" Handles HTTP GET.
|
|
"""
|
|
command = environs.getVar("httpCommand")
|
|
if command == "GET":
|
|
results = self.getResults()
|
|
if results is None:
|
|
raise faults.PathNotFound("") # FIXME: display error message
|
|
output = "\n".join([value.asString() for value in results]).strip()
|
|
logs.debug("results: %s" % output)
|
|
try:
|
|
doc = libxml2.readDoc(output, None, None, 0)
|
|
except libxml2.treeError:
|
|
return super(XQuery, self).styled()
|
|
element = self.newElement(doc.getRootElement())
|
|
element.doHttpGet()
|
|
else:
|
|
raise faults.PathNotFound("")
|
|
|
|
|
|
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_CDB
|
|
# |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)
|
|
document = manager.createDocument()
|
|
document.setContent(doc.serialize())
|
|
document.setName(documentNode.prop("name"))
|
|
label = documentNode.prop("label")
|
|
if label:
|
|
document.setMetaData(
|
|
"http://www.sleepycat.com/2002/dbxml",
|
|
"label",
|
|
dbxml.XmlValue(label)
|
|
)
|
|
container.putDocument(document, 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()
|
|
|