Support for Berkeley DB XML 2.1.

This commit breaks existing code in third party apps such as Amedes.
This commit is contained in:
sebd 2005-05-09 14:47:45 +00:00
parent 3e3e524358
commit 2a65f76d3e
3 changed files with 205 additions and 121 deletions

View File

@ -27,7 +27,6 @@
import os
import bsddb.db
import dbxml
import libxml2
import urllib
@ -53,63 +52,42 @@ class DbXmlContainerHolder(dataholders.DataHolder):
isTemporary = False
isUriDirectory = True
mimeType = "application/x-dbxml"
queryContext = None
updateContext = None
def __del__(self):
if self.container is not None:
if self.container.isOpen():
logs.info("DbXml Container was not closed! Closing it.")
self.container.close(0)
def __init__(self, *arguments, **keywords):
if self._dbxmlContainerHolderInited:
return
self._dbxmlContainerHolderInited = True
def __init__(self, container, *arguments, **keywords):
super(DbXmlContainerHolder, self).__init__(*arguments, **keywords)
# FIXME : we assume self.getAbsolutePath() is a correct .dbxml filename
self.container = dbxml.XmlContainer(None, self.getAbsolutePath())
self.queryContext = dbxml.XmlQueryContext()
self.container = container
self.queryContext = container.getManager().createQueryContext()
for ns in namespaces.nsList:
self.queryContext.setNamespace(ns.name, ns.uri)
self.updateContext = container.getManager().createUpdateContext()
def addDocument(self, documentSource, name=""):
def addDocument(self, contents, name=""):
""" appends a document to the container
returns the new XmlDocument
returns the new XmlDocument name
"""
doc = dbxml.XmlDocument()
doc.setContent(documentSource)
doc.setName(name)
self.container.open(None, 0)
try:
self.container.putDocument(None, doc)
name = self.container.putDocument(name, contents, self.updateContext, name and DBXML_GEN_NAME or 0)
except RuntimeError, e:
logs.error("Could not put document in container: %s" % e)
logs.debug(doc.getContent())
self.container.close(0)
logs.debug(contents,)
raise
self.container.close(0)
return doc
return name
def close(self):
self.container.close(0)
def deleteDocument(self, documentId):
def deleteDocument(self, name):
""" removes a document from the container
Handles HTTP GET.
"""
self.container.open(None, 0)
self.container.deleteDocument(None, documentId)
self.container.close(0)
self.container.deleteDocument(name, self.updateContext)
return self.doHttpGet()
def docSelect(self, xpathExpression, *uriPathFragments):
"""
Handles HTTP GET.
"""
command = environs.getVar("httpCommand")
if command != "GET":
raise faults.PathNotFound("")
@ -129,11 +107,11 @@ class DbXmlContainerHolder(dataholders.DataHolder):
return super(DbXmlContainerHolder, self).doHttpGet()
def generateXml(self, layout):
result = self.getDbXmlDocumentResultFromXpath("*")
result = self.getDbXmlDocumentResultFromXpath("/*")
return result.generateXml(layout)
def generateXmlDocument(self):
result = self.getDbXmlDocumentResultFromXpath("*")
result = self.getDbXmlDocumentResultFromXpath("/*")
return result.generateXmlDocument()
def getContainer(self):
@ -152,19 +130,17 @@ class DbXmlContainerHolder(dataholders.DataHolder):
results = self.getResultFetchedFromXpath(xpathExpression)
return results.size()
def getDbXmlDocument(self, id):
""" returns container document number <id>
def getDbXmlDocument(self, name):
""" returns container document <name>
the return type is dbxml.DbXmlDocument
the return type is dbxml.XmlDocument
or None if the document doesn't exist
"""
document = None
self.container.open(None, dbxml.DB_RDONLY)
try:
document = self.container.getDocument(None, int(id))
document = self.container.getDocument(name, 0)
except RuntimeError:
pass
self.container.close(0)
return document
def getDbXmlDocumentResultFromXpath(self, xpathExpression):
@ -172,11 +148,12 @@ class DbXmlContainerHolder(dataholders.DataHolder):
resultNode = resultDocument.node.newTextChild(None, "dbXmlDocumentResult", None)
namespace = resultNode.newNs(namespaces.yep.uri, namespaces.yep.name)
resultNode.setNs(namespace)
results = self.getResultFetchedFromXpath(xpathExpression)
results = self.getResultCursorFromXpath(xpathExpression)
results.reset()
for value in results:
document = value.asDocument()
childNode = resultNode.newChild(None, "item", None)
childNode.newProp("id", str(document.getID()))
childNode.newProp("id", self.getActionUri(document.getName()))
childNode.newProp("name", document.getName())
return resultDocument
@ -196,7 +173,17 @@ class DbXmlContainerHolder(dataholders.DataHolder):
childNode.addChild(doc.getRootElement())
return resultDocument
def getIds(self, xpathExpression = "*"):
def getFileSystemPath(self):
return os.path.normpath(
os.path.join(
self.container.getManager().getHome(), self.container.getName()
)
)
def getConfigDoc(self):
return None
def getIds(self, xpathExpression = "/*"):
""" returns an id list of all documents
id is not the numeric dbxml:id, but the Uri of the document
@ -205,9 +192,9 @@ class DbXmlContainerHolder(dataholders.DataHolder):
"""
uriBase = self.getUriAbsolutePath()
results = self.getResultFetchedFromXpath(xpathExpression)
return ["%s/%s" % (uriBase, value.asDocument().getID()) for value in results]
return ["%s/%s" % (uriBase, value.asDocument().getName()) for value in results]
def getNames(self, xpathExpression = "*"):
def getNames(self, xpathExpression = "/*"):
""" returns a hash {id: name} of all documents
id is not the numeric dbxml:id, but the Uri of the document
@ -219,25 +206,23 @@ class DbXmlContainerHolder(dataholders.DataHolder):
results = self.getResultFetchedFromXpath(xpathExpression)
for value in results:
document = value.asDocument()
uri = "%s/%s" % (uriBase, document.getID())
name = document.getName()
uri = "%s/%s" % (uriBase, name)
names[uri] = name
return names
def getPresence(self, xpathExpression):
""" Returns whether at least one document matches xpathExpression.
"""
presence = False
for value in self.getResultCursorFromXpath(xpathExpression):
presence = True
break
self.close()
return presence
results = self.getResultCursorFromXpath(xpathExpression)
for value in result:
return True
return False
def getIdsAndNames(self, xpathExpression = "*"):
def getIdsAndNames(self, xpathExpression = "/*"):
""" returns a tuple (id, name) list of all documents
id is not the numeric dbxml:id, but the Uri of the document
id is the Uri of the document
argument xpathExpression can be specified to restrict the list
"""
@ -246,52 +231,40 @@ class DbXmlContainerHolder(dataholders.DataHolder):
results = self.getResultFetchedFromXpath(xpathExpression)
for value in results:
document = value.asDocument()
uri = "%s/%s" % (uriBase, document.getID())
uri = "%s/%s" % (uriBase, document.getName())
name = document.getName()
documentList.append(
(uri, name)
)
return documentList
def getIdsOnly(self, xpathExpression = "*"):
""" returns an id list of all documents
id is the numeric dbxml:id
def getIdsOnly(self, xpathExpression = "/*"):
""" returns a name list of all documents
argument xpathExpression can be specified to restrict the list
"""
results = self.getResultFetchedFromXpath(xpathExpression)
return [value.asDocument().getID() for value in results]
return [value.asDocument().getName() for value in results]
def getResultCursorFromXpath(self, xpathExpression, returnType = dbxml.XmlQueryContextPtr.ResultDocuments):
def getResultCursorFromXpath(self, xpathExpression, returnType = dbxml.XmlQueryContext.LiveValues):
""" Returns a raw dbxml.XmlResults instance, leaving the data in the container.
Result contents are not loaded in memory, but waiting to be fetched iteratively.
The calling program MUST call close() after this method, since we can not close the container here.
Set returnType argument to dbxml.XmlQueryContextPtr.ResultValues to get nodes rather than documents
"""
context = self.queryContext
context.setReturnType(returnType)
context.setEvaluationType(dbxml.XmlQueryContext.Lazy)
self.container.open(None, dbxml.DB_RDONLY)
results = self.container.queryWithXPath(None, xpathExpression, context)
results = self.container.getManager().query("collection('%s')%s" % (self.container.getName(), xpathExpression), context)
return results
def getResultFetchedFromXpath(self, xpathExpression, returnType = dbxml.XmlQueryContextPtr.ResultDocuments):
def getResultFetchedFromXpath(self, xpathExpression, returnType = dbxml.XmlQueryContext.LiveValues):
""" Returns a raw dbxml.XmlResults instance, with all data fetched from the container.
All data is loaded in memory.
Consider using getResultCursorFromXpath instead of this method if you expect a large result.
Set returnType argument to dbxml.XmlQueryContextPtr.ResultValues to get nodes rather than documents
"""
context = self.queryContext
context.setReturnType(returnType)
context.setEvaluationType(dbxml.XmlQueryContext.Eager)
self.container.open(None, dbxml.DB_RDONLY)
results = self.container.queryWithXPath(None, xpathExpression, context)
self.container.close(0)
results = self.container.getManager().query("collection('%s')%s" % (self.container.getName(), xpathExpression), context)
results.reset()
return results
@ -302,31 +275,26 @@ class DbXmlContainerHolder(dataholders.DataHolder):
return [value.asString() for value in results]
def list(self):
return self.docSelect("*")
return self.docSelect("/*")
def nodeSelect(self, xpathExpression, *uriPathFragments):
"""
Handles HTTP GET.
"""
command = environs.getVar("httpCommand")
if command != "GET":
raise faults.PathNotFound("")
result = self.getDbXmlNodeResultFromXpath(xpathExpression)
return result.checkAccessAndWalk(uriPathFragments, command, environs.getVar("instruction"))
def parsableXpath(self, xpath):
""" returns True if the given xpath expression is parsable, False otherwise
"""
parsable = True
self.container.open(None, dbxml.DB_RDONLY)
try:
self.container.parseXPathExpression(None, xpath, self.queryContext)
self.container.getManager().prepare("collection('%s')%s" % (self.container.getName(), xpath), self.queryContext)
except RuntimeError:
parsable = False
self.container.close(0)
return parsable
return False
return True
def walkToAction(self, uriPathFragments, command = None, instruction = None):
actionName = uriPathFragments[0]
@ -380,12 +348,11 @@ class DbXmlContainerHolder(dataholders.DataHolder):
def walkToItem(self, uriPathFragments, command = None, instruction = None):
itemName = uriPathFragments[0]
if itemName.isdigit():
document = self.getDbXmlDocument(itemName)
if document:
documentStation = DbXmlDocumentStation(
document, previous = self, uriPathFragment = itemName)
return documentStation, uriPathFragments[1:]
document = self.getDbXmlDocument(itemName)
if document:
documentStation = DbXmlDocumentStation(
document, previous = self, uriPathFragment = itemName)
return documentStation, uriPathFragments[1:]
return super(DbXmlContainerHolder, self).walkToItem(uriPathFragments, command, instruction)
@ -393,7 +360,7 @@ class DbXmlDocumentConstructorStation(stations.HolderConstructorStation):
def walk(self, uriPathFragments, command = None, instruction = None):
namespaceUri = self.elementFeature.namespaceUri
elementName = self.elementFeature.elementName
document = dbxml.XmlDocument()
document = self.getParent().container.getManager().createDocument()
document.setContent("""\
<?xml version="1.0"?>
<%(elementName)s xmlns="%(namespaceUri)s"/>
@ -432,10 +399,7 @@ class DbXmlDocumentStation(stations.AbstractStation, documents.AbstractDocument)
def delete(self):
""" Deletes the document from its container.
"""
container = self.getParent().getContainer()
container.open(None, 0)
container.deleteDocument(None, self.dbxmlDocument)
container.close(0)
self.getParent().getContainer().deleteDocument(self.dbxmlDocument.getName(), self.getParent().updateContext)
def doHttpGet(self):
if not self.getInRawMode():
@ -464,9 +428,7 @@ class DbXmlDocumentStation(stations.AbstractStation, documents.AbstractDocument)
return super(DbXmlDocumentStation, self).doHttpPost()
def generatePlainText(self):
# FIXME: The following line assumes that getContentAsString returns an
# unicode string. If this is not the case, append ".decode("UTF-8")".
return self.dbxmlDocument.getContentAsString()
return self.dbxmlDocument.getContent()
def generateXml(self, layout):
rootElement = self.getRootElement()
@ -480,6 +442,9 @@ class DbXmlDocumentStation(stations.AbstractStation, documents.AbstractDocument)
return rootElement.generateXmlDocument()
return super(DbXmlDocumentStation, self).generateXmlDocument()
def getAbsolutePath(self):
return os.path.normpath(os.path.join(self.getParent().getAbsolutePath(), self.dbxmlDocument.getName()))
def getBind(self, context, modelContext):
return None
@ -490,7 +455,7 @@ class DbXmlDocumentStation(stations.AbstractStation, documents.AbstractDocument)
return self
def getLocalId(self):
return str(self.dbxmlDocument.getID())
return self.dbxmlDocument.getName()
def getModelId(self):
return None
@ -504,7 +469,7 @@ class DbXmlDocumentStation(stations.AbstractStation, documents.AbstractDocument)
return publicName
def outputHttpSource(self):
data = self.dbxmlDocument.getContentAsString()
data = self.dbxmlDocument.getContent()
dataHolder = self.getDataHolder()
environs.getVar("httpRequestHandler").outputData(
data, mimeType = dataholders.mimeTypes[".xml"],
@ -512,40 +477,40 @@ class DbXmlDocumentStation(stations.AbstractStation, documents.AbstractDocument)
def save(self):
self.dbxmlDocument.setContent(self.serialize())
name = self.getPublicName()
if not name:
element = self.getRootElement()
try:
name = element.getLabel()
except AttributeError:
name = ""
self.dbxmlDocument.setName(name)
container = self.getParent().getContainer()
container.open(None, 0)
if self.getLocalId() == "0":
if self.isTemporary:
name = self.getPublicName()
if name is None:
element = self.getRootElement()
try:
name = element.getLabel()
except AttributeError:
pass
if name is not None:
self.dbxmlDocument.setName(name)
try:
container.putDocument(None, self.dbxmlDocument)
container.putDocument(
self.dbxmlDocument,
self.getParent().updateContext,
name is None and dbxml.DBXML_GEN_NAME or 0
)
except RuntimeError, e:
logs.error("Could not put document in container: %s" % e)
logs.debug(self.dbxmlDocument.getContent())
container.close(0)
raise
self.uriPathFragment = self.getLocalId()
if self.isTemporary:
del self.isTemporary
del self.isTemporary
else:
try:
container.updateDocument(None, self.dbxmlDocument)
container.updateDocument(self.dbxmlDocument, self.getParent().updateContext)
except RuntimeError, e:
logs.error("Could not update document in container: %s" % e)
logs.debug(self.dbxmlDocument.getContent())
container.close(0)
raise
container.close(0)
def setPermanentLocalId(self):
# We do not need to set a permanentLocalId here.
# The document will have it's own id once inside the dbxml.
# The document will have it's own id once inside the container.
pass
def walk(self, uriPathFragments, command = None, instruction = None):

View File

@ -0,0 +1,119 @@
# -*- 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 sys
import bsddb3.db as db
import dbxml
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.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,
)
return item, uriPathFragments[1:]
return super(DbXmlDatabase, self).walkToItem(uriPathFragments, command, instruction)
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)
dbHome = environs.getVar("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
)
DbXmlDatabase._manager = dbxml.XmlManager(DbXmlDatabase._environment, dbxml.DBXML_ADOPT_DBENV)
DbXmlDatabase._containers = {}
for name in environs.getVar("configuration").getConfigList("""yep:module[@name="%s"]/yep:container/@name""" % __init__.__module__):
try:
DbXmlDatabase._containers[name] = DbXmlDatabase._manager.openContainer(
name, 0
|db.DB_CREATE
|dbxml.DBXML_CHKSUM
)
except RuntimeError:
logs.error("Could not open container %s" % name)
__del__()
raise
def __del__():
""" Closes containers and the database environment.
"""
del DbXmlDatabase._containers
DbXmlDatabase._environment.close()

View File

@ -13,7 +13,7 @@
<xforms:label yep:translatable="true">Documents</xforms:label>
<xforms:repeat nodeset="yep:item">
<xforms:trigger>
<xforms:label><xforms:output ref="@id"/>. <xforms:output ref="@name"/></xforms:label>
<xforms:label><xforms:output ref="@name"/></xforms:label>
<xforms:load ev:event="DOMActivate" ref="@id" show="replace"/>
</xforms:trigger>
</xforms:repeat>