Skip to content
Snippets Groups Projects
cache.xqm 4.77 KiB
Newer Older
xquery version "3.1" encoding "UTF-8";

(:~
 : XQuery module for caching documents and collections
~:)
module namespace cache="http://xquery.weber-gesamtausgabe.de/modules/cache";

import module namespace functx="http://www.functx.com";
import module namespace wega-util-shared="http://xquery.weber-gesamtausgabe.de/modules/wega-util-shared" at "wega-util-shared.xqm";

declare variable $cache:TOO_MANY_PARAMETERS_ERROR := QName("http://xquery.weber-gesamtausgabe.de/modules/cache", "TooManyParametersError");
declare variable $cache:UNSUPPORTED_PARAMETER_VALUE_ERROR := QName("http://xquery.weber-gesamtausgabe.de/modules/cache", "UnsupportedParameterValueError");

(:~
 : A caching function for documents (XML and binary)
 :
 : @author Peter Stadler
 : @param $docURI the database URI of the document
 : @param $callBack a function to create the document content when the document is outdated or not available
 : @param $lease an xs:dayTimeDuration value of how long the cache should persist, e.g. P999D (= 999 days). 
 :          Alternatively, $lease can be a callback function – then one argument (the last change date of the file as xs:date()?) will be provided 
 :          and the function should return xs:boolean
 : @return the cached document
 :)
declare function cache:doc($docURI as xs:string, $callback as function() as item(), $callback-params as item()*, $lease as item()?, $onFailure as function() as item()*) as item()* {
    let $fileName := functx:substring-after-last($docURI, '/')
    let $collection := functx:substring-before-last($docURI, '/')
    let $currentDateTimeOfFile := 
        if(wega-util-shared:doc-available($docURI)) then xmldb:last-modified($collection, $fileName)
        else if(util:binary-doc-available($docURI)) then xmldb:last-modified($collection, $fileName)
        else ()
    let $updateNecessary := 
        typeswitch($lease)
        case xs:dayTimeDuration return
            $currentDateTimeOfFile + $lease lt current-dateTime()
            or empty($lease) 
            or empty($currentDateTimeOfFile)
        case function() as xs:boolean return $lease($currentDateTimeOfFile)
        default return error($cache:UNSUPPORTED_PARAMETER_VALUE_ERROR, 'The parameter value for $lease must be xs:dayTimeDuration()? or a function reference which must take exactly one argument.')
    return 
	   if($updateNecessary) then (
            let $content := 
                if(count($callback-params) eq 0) then $callback()
                else if(count($callback-params) eq 1) then $callback($callback-params)
                else if(count($callback-params) eq 2) then $callback($callback-params[1], $callback-params[2])
                else if(count($callback-params) eq 3) then $callback($callback-params[1], $callback-params[2], $callback-params[3])
                else if(count($callback-params) eq 4) then $callback($callback-params[1], $callback-params[2], $callback-params[3], $callback-params[4])
                else error($cache:TOO_MANY_PARAMETERS_ERROR, 'Too many arguments to callback function within cache:doc(). A maximum of 4 arguments is supported')
            let $mime-type := wega-util-shared:guess-mimeType-from-suffix(functx:substring-after-last($docURI, '.'))
            let $store-file := cache:store-file($collection, $fileName, $content, $mime-type, $onFailure)
            return 
                if(util:binary-doc-available($store-file)) then util:binary-doc($store-file)
                else if(wega-util-shared:doc-available($store-file)) then doc($store-file) 
                else ()
        )
        else if(util:binary-doc-available($docURI)) then util:binary-doc($docURI)
        else if(wega-util-shared:doc-available($docURI)) then doc($docURI)
        else ()
};

(:~
 : Store some content as file in the db
 : (Helper function for cache:doc())
 : 
 : @author Peter Stadler
 : @param $collection the collection to put the file in. If it does not exist, it will be created  
 : @param $fileName the filename of the to be created resource with filename extension
 : @param $contents the content to store. Either a node, an xs:string, a Java file object or an xs:anyURI 
 : @return Returns the path to the newly created resource, empty sequence otherwise
 :)
declare %private function cache:store-file($collection as xs:string, $fileName as xs:string, $contents as item(), $mime-type as xs:string, $onFailure as function() as item()*) as xs:string? {
    let $createCollection := 
        for $coll in tokenize($collection, '/')
        let $parentColl := substring-before($collection, $coll)
        return 
            if(xmldb:collection-available($parentColl || '/' || $coll)) then ()
            else xmldb:create-collection($parentColl, $coll)
    return
        try { xmldb:store($collection, $fileName, $contents, $mime-type) }
        catch * { $onFailure($err:code, $err:description) }
};