Loading TOC...
Scripting Administrative Tasks Guide (PDF)

Scripting Administrative Tasks Guide — Chapter 5

Sample Configuration Program

The sample configuration program uses the Admin APIs to construct a MarkLogic Server configuration that supports the Oscars Explorer application described in Using the Oscars Sample Application in the Application Builder Developer's Guide.

The sample configuration program is located in MarkLogic/Samples/admin. The objective of this sample program is to educate users on the use of the Admin APIs. Though many parts of the code can be reused, the modules in this example are structured to highlight the use of the Admin API functions. You, however, may want to structure your program to hide the complexities of the APIs and define all of your configuration variables in a separate module or XML file, so that all changes to the configuration can be made in one place.

The topics in this section are:

How to Execute the Sample Configuration Program

Before you can run the Sample Configuration Program, you must create an HTTP App Server that has a root path to the location of the program files.

To create an App Server for the Sample Configuration Program, do the following:

  1. Open the Admin Interface and create a new HTTP App Server, as described in Creating a New HTTP Server in the Administrator's Guide. Set the App Server root to Samples/admin and assign it a unique port number, such as port 8055 in this example:

  2. In a browser window, open the install.xqy file at the App Server URL. For example:
    http://localhost:8055/install.xqy

    You should see output like:

    Server is Configured
         Done loading XML data into Sample-Database
         Done loading application data into Sample-Modules
    Role IDs are:
    1067450794701390527
    10780693773632192097
    8870937581372109438
    User IDs are:
    3884989588766400714
    Users and Roles are Configured
  3. To start the sample application, enter: http://localhost:8016. You should see the Oscars Explorer screen:

Structure of the Sample Configuration Program

The sample configuration program consists of the modules illustrated below:

  • install.xqy is the main module that calls the configuration-specific functions in the configure-server.xqy, configure-security.xqy, and load-data.xqy modules to create the sample configuration and load the data into the databases.
  • uninstall.xqy is the main module that calls the functions in the configure-server.xqy and configure-security.xqy modules to remove the sample configuration.
  • configure-server.xqy defines the configuration-specific functions that call the implementation functions in the groups.xqy and databases.xqy modules to create and configure the app server and databases needed to support the Oscars Explorer application.
  • configure-security.xqy contains the implementation functions that create and remove the roles and users. The functions in this module call the functions in the MarkLogic Server APIs.
  • groups.xqy contains the implementation functions for creating, configuring, and removing groups and servers. The functions in this module call the functions in the MarkLogic Server APIs and are general enough to be used to create configurations to support applications other than Oscars Explorer.
  • databases.xqy contains the implementation functions for creating, configuring, and removing forests and databases. The functions in this module call the functions in the MarkLogic Server APIs and are general enough to be used to create configurations to support applications other than Oscars Explorer.
  • load-data.xqy contains the functions that load the content database and application module database. This module exists only to load the data in order to get the Oscars application working and its implementation is outside the scope of this document.
  • setup-library.xqy contains the generic code for logging status and errors.

install.xqy

The install.xqy module is the main module that calls the functions in the configure-server.xqy, configure-security.xqy, and loadData.xqy modules to configure the MarkLogic Server and load the data needed to support the Oscars Explorer application. The functions in the configure-security.xqy and loadData.xqy modules are executed against their respective databases by calling them within xdmp:eval functions with the <database> option. For more detail on executing queries in a database other than that assigned to the App Server, see Executing Queries in Select Databases.

The loadData.xqy module is needed to load the application and content data into the databases, but its implementation is not within the scope of this guide.

xquery version "1.0-ml";
import module namespace conf-server =
  "http://marklogic.com/sampleConfig/conf-server"
    at "configure-server.xqy";
import module namespace grp="http://marklogic.com/sampleConfig/groups"
    at "groups.xqy";
import module namespace admin = "http://marklogic.com/xdmp/admin" 
    at "/MarkLogic/admin.xqy";
(: Get the cluster configuration :)
let $config := admin:get-configuration()
(: Get the Security database used by the Admin AppServer :)
let $securityDB := grp:get-security-db($config, "Default", "Admin")
(: Create and configure the databases and server :)
let $config := conf-server:create-forests($config)
let $config := conf-server:create-databases($config)
let $config := conf-server:create-server($config)
(: These variables define the functions that create new roles and 
   users and load the data into the databases. Each function is 
   then executed by xdmp:eval against a specific database :)
let $create-roles:= '
   xquery version "1.0-ml";
   import module namespace conf-security=
     "http://marklogic.com/sampleConfig/conf-security" at
        "configure-security.xqy";
   conf-security:configure-roles()'
let $create-users := '
   xquery version "1.0-ml";
   import module namespace conf-security=
     "http://marklogic.com/sampleConfig/conf-security"
        at "configure-security.xqy";
   conf-security:configure-users()'
let $load-XML := '
   xquery version "1.0-ml";
   import module namespace load-data =
     "http://marklogic.com/sampleConfig/load-data" at "loadData.xqy";
   load-data:load-sample-XML( 
"C:/Program Files/MarkLogic/Assets/appbuilder/templates/oscars/data")'
let $load-modules := '
   xquery version "1.0-ml";
   import module namespace setup =
     "http://marklogic.com/sampleConfig/setup-lib"
        at "setup-library.xqy";
   import module namespace load-data =
     "http://marklogic.com/sampleConfig/load-data" at "loadData.xqy";
   load-data:load-sample-module($setup:LOADDATA)'
return (
   admin:save-configuration($config),
   "Server is Configured",
(: Call the above functions in their respective databases :)
   xdmp:eval(
     $load-XML,
     (),
     <options xmlns="xdmp:eval">
       <database>{xdmp:database("Sample-Database")}</database>
     </options>),
   xdmp:eval(
     $load-modules,
     (),
     <options xmlns="xdmp:eval">
       <database>{xdmp:database("Sample-Modules")}</database>
     </options>),
   "Role IDs are:",
   xdmp:eval(
     $create-roles,
     (),
     <options xmlns="xdmp:eval">
       <database>{$securityDB}</database>
     </options>),
   "User IDs are:",
   xdmp:eval(
     $create-users,
     (),
     <options xmlns="xdmp:eval">
       <database>{$securityDB}</database>
     </options>),
   "Users and Roles are Configured"
)

uninstall.xqy

The uninstall.xqy module is the main module that calls the functions in the configure-server.xqy and configure-security.xqy modules to remove the MarkLogic Server objects created by the install.xqy module. The functions in the configure-security.xqy module are executed against their respective databases by calling them within xdmp:eval functions with the <database> option.

xquery version "1.0-ml";
import module namespace conf-server =
  "http://marklogic.com/sampleConfig/conf-server" 
     at "configure-server.xqy";
import module namespace grp="http://marklogic.com/sampleConfig/groups"
     at "groups.xqy";
import module namespace admin = "http://marklogic.com/xdmp/admin" 
     at "/MarkLogic/admin.xqy";
let $remove-users := '
  xquery version "1.0-ml";
import module namespace conf-security =
  "http://marklogic.com/sampleConfig/conf-security" 
     at "configure-security.xqy";
conf-security:remove-users()'
let $remove-roles := '
  xquery version "1.0-ml";
import module namespace conf-security =
  "http://marklogic.com/sampleConfig/conf-security" 
     at "configure-security.xqy";
conf-security:remove-roles()'
let $config := admin:get-configuration()
let $securityDB := grp:get-security-db($config, "Default", "Admin")
return (
  xdmp:eval($remove-roles, (),
    <options xmlns="xdmp:eval">
      <database>{$securityDB}</database>
    </options>),
  xdmp:eval($remove-users, (),
    <options xmlns="xdmp:eval">
      <database>{$securityDB}</database>
    </options>),
"Removed Users and Roles",
conf-server:remove-config(), 
"Removed Server Configuration"
)

configure-server.xqy

The configure-server.xqy module defines the high-level, configuration-specific functions that call the more 'generic' functions in the groups.xqy and databases.xqy modules to create, configure, and delete the forests, databases and app server used by the Oscars Explorer application.

The functions in this module are:

Before defining the functions, we define this module's namespace as conf-server and import the following modules:

xquery version "1.0-ml";
(: Create/Remove Server/Database Configuration :)
module namespace conf-server =
  "http://marklogic.com/sampleConfig/conf-server";
import module namespace admin = "http://marklogic.com/xdmp/admin" 
      at "/MarkLogic/admin.xqy";
import module namespace db=
  "http://marklogic.com/sampleConfig/database" at "databases.xqy";
import module namespace grp=
  "http://marklogic.com/sampleConfig/groups" at "groups.xqy";
import module namespace setup=
  "http://marklogic.com/sampleConfig/setup-lib"
    at "setup-library.xqy";

create-forests function

The create-forests function calls the functions in the databases.xqy module to create the forests:

declare function create-forests($config as element(configuration))
{
try {
    (: Create forests :)
     let $config := db:create-forest(
        $config, 
        "SampleDB-Forest")
     let $config := db:create-forest(
        $config, 
        "SampleModules-Forest")
    (: Save and return the configuration :)
     return (
         admin:save-configuration($config),
         $config
     )
} catch($e) {
     setup:log($e)
   }
};

create-databases function

The create-databases function calls the functions in the databases.xqy module to create and configure the databases to hold the application and content data for the Oscars Explorer application.

declare function create-databases($config as element(configuration))
{
try {
    (: Create databases :)
     let $config := db:create-database(
        $config, 
        "Sample-Database")
     let $config := db:create-database(
        $config, 
        "Sample-Modules")
    (: Attach forests to databases :)
     let $config := db:attach-forests(
        $config, 
        "Sample-Database", 
        "SampleDB-Forest")
     let $config :=db:attach-forests(
         $config, 
         "Sample-Modules", 
         "SampleModules-Forest")
    (: Add a 'wiki-suggest' field to the Sample-Database :)
     let $config := db:add-field(
        $config, 
        "Sample-Database", 
        "wiki-suggest")
    (: Add included elements to 'wiki-suggest' field :)
     let $config := db:add-field-included(
        $config,
        "Sample-Database",
        "wiki-suggest",
        "name")
     let $config := db:add-field-included(
        $config,
        "Sample-Database",
        "wiki-suggest",
        "film-title")
    (: Add indexes to the Sample-Database :)
     let $config := db:add-range-element-index(
        $config,
        "Sample-Database",
        "string",
        "http://marklogic.com/wikipedia",
        "name")
     let $config := db:add-range-element-index(
        $config,
        "Sample-Database",
        "string",
        "http://marklogic.com/wikipedia",
        "film-title")
     let $config := db:add-range-element-attribute-index(
        $config, 
        "Sample-Database", 
        "gYear", 
        "http://marklogic.com/wikipedia",
        "nominee", 
        "", 
        "year",
        "")
     let $config := db:add-range-element-attribute-index(
        $config,
        "Sample-Database", 
        "string", 
        "http://marklogic.com/wikipedia",
        "nominee",
        "", 
        "award", 
        "http://marklogic.com/collation/")
     let $config := db:add-range-element-attribute-index(
        $config, 
        "Sample-Database", 
        "string", 
        "http://marklogic.com/wikipedia",
        "nominee", 
        "", 
        "winner", 
        "http://marklogic.com/collation/")
    (: Save and return the configuration :)
     return (
         admin:save-configuration($config),
         $config
     )
} catch($e) {
     setup:log($e)
   }
};

create-server function

The create-server function calls the functions in the groups.xqy module to create and configure an HTTP App Server to support the Oscars Explorer application. Note that, rather than define the app server settings as parameters, the create-server function in the groups.xqy module parses the settings from the $server-config XML node defined in this function.

declare function create-server($config as element(configuration))
{
try {
(: Create a node containing the basic server settings :)
     let $server-config :=  
        <http-server>
           <name>Sample-Server</name>
           <port>8016</port>
           <root>application/</root>
           <modules>Sample-Modules</modules>
           <database>Sample-Database</database>
        </http-server>
(: Create the server :)
     let $config := grp:create-server(
        $config, 
        $server-config, 
        "Default")
(: Set server error handler and url rewriter :)
     let $config := grp:set-error-handler(
        $config,
        "Default",
        "Sample-Server",
        "error.xqy")
     let $config := grp:set-url-rewriter(
        $config,
        "Default",
        "Sample-Server",
        "rewrite.xqy")
(: Save and return the configuration :)
     return (
         admin:save-configuration($config),
         $config
     )
} catch($e) {
     setup:log($e)
   }
};

remove-config function

The remove-config function calls functions in the groups.xqy and databases.xqy modules to remove the forests, databases, and app server created by the create-server and create-databases functions.

declare function remove-config()
{
try {
   (: Remove the app server :)
    let $config := admin:get-configuration()
    let $config := grp:delete-server(
      $config, 
      "Sample-Server", 
      "Default")
    let $hosts1 := admin:save-configuration-without-restart($config)
   (: Remove the databases :)
    let $config := db:delete-database($config, "Sample-Database")
    let $config := db:delete-database($config, "Sample-Modules")
    let $hosts2 := admin:save-configuration-without-restart($config)
   (: Remove the forests :)
    let $config := db:delete-forest($config, "SampleDB-Forest")
    let $config := db:delete-forest($config, "SampleModules-Forest")
   (: Save the configuration and restart MarkLogic server :)
    return (admin:save-configuration($config),
         if ($hosts1, $hosts2) 
         then (
            "Restarting Hosts -- wait until restart has completed before accessing MarkLogic Server",
            admin:restart-hosts(($hosts1, $hosts2))
         ) 
         else ()
    )
} catch($e) {
     setup:log($e)
   }
};

groups.xqy

The groups.xqy module defines functions that call the admin functions that create, configure, and remove groups and app servers. The functions in this module are more 'generic' than the configuration-specific functions in the configure-server.xqy module that call them. Consequently, the functions in this module could be applied to a variety of configurations.

Each function in this module first checks for the presence of the object it is to create or remove before taking any action. This is done to enable partial changes to be made to the configuration without the entire transaction failing, as described in Making Transactions Idempotent.

Before defining the functions, we define this module's namespace as grp and import the following modules:

xquery version "1.0-ml";
module namespace grp = "http://marklogic.com/sampleConfig/groups";
declare namespace error = "http://marklogic.com/xdmp/error";
import module namespace admin = "http://marklogic.com/xdmp/admin"
    at "/MarkLogic/admin.xqy";
import module namespace setup =
  "http://marklogic.com/sampleConfig/setup-lib"
    at "setup-library.xqy";

create-group function

The create-group function first checks to see whether the specified groups exists. If not, it calls the admin:group-create function to create the new group. This function is not called any function in the configure-server.xqy module, but could be used by other high-level modules to create another configuration.

declare function create-group(
  $config as element(configuration), 
  $NewGroup as xs:string) 
{
try {
     let $log := setup:log(fn:concat("creating group :", $NewGroup))
(: Get all of the existing groups :)
     let $ExistingGroups :=
         for $id in admin:get-group-ids($config)
            return admin:group-get-name($config, $id)
(: Check to see if group already exists. If not, create new group :)
     let $config :=
        if ($NewGroup = $ExistingGroups) 
        then $config
        else admin:group-create($config, $NewGroup)
     return $config
} catch($e) {
     setup:log($e)
   }
};

create-server function

The create-server function first checks to see whether the specified app server exists. If not, it calls the admin functions to create and configure the new app server. The app server type (HTTP, WebDAV, XDBC) is specified by the root element of the $server-config node passed from the configure-server.xqy module.

declare function create-server(
  $config as element(configuration), 
  $server as node(), 
  $group-name as xs:string) 
{
try {
     let $group-id := admin:group-get-id($config, $group-name)
     let $NewServer := fn:data($server/name)
     let $root := fn:data($server/root)
     let $port := xs:integer(fn:data($server/port))
     let $modules := fn:data($server/modules)
     let $module-id := 
       if($modules eq "filesystem") 
       then 0 
       else xdmp:database($modules)
     let $database-id := xdmp:database(fn:data($server/database))
(: Get all of the existing servers :)
     let $ExistingServers :=
         for $id in admin:get-appserver-ids($config)
            return admin:appserver-get-name($config, $id)
(: Check to see if server already exists. If not, create new server :)
     let $config :=
        if ($NewServer = $ExistingServers) 
        then $config
        else
          if (fn:name($server) eq "http-server") 
          then 
            create-http-server(
               $config, 
               $group-id, 
               $NewServer, 
               $root, 
               $port, 
               $module-id, 
               $database-id)
          else if(fn:name($server) eq "xdbc-server") then 
            create-xdbc-server(
               $config, 
               $group-id, 
               $NewServer, 
               $root, 
               $port, 
               $module-id, 
               $database-id)
          else if(fn:name($server) eq "webdav-server") then 
            create-webdav-server(
               $config, 
               $group-id, 
               $NewServer, 
               $root, 
               $port, 
               $database-id)
          else ()
     return $config
} catch($e) {
     setup:log($e)
   }
};
declare function create-xdbc-server(
  $config as element(configuration), 
  $group-id, 
  $server-name, 
  $root, 
  $port, 
  $module-id, 
  $database-id) 
{
try {
     let $config := admin:xdbc-server-create(
       $config, 
       $group-id, 
       $server-name, 
       $root, 
       $port, 
       $module-id, 
       $database-id)
     let $log := setup:log(fn:concat(
       "Creating XDBC server ",
       $server-name))
     return  $config
} catch($e) {
     setup:log($e)
   }
};
declare function create-webdav-server(
  $config as element(configuration), 
  $group-id, 
  $server-name, 
  $root, 
  $port, 
  $database-id) 
{
try {
     let $config := admin:webdav-server-create(
       $config, 
       $group-id, 
       $server-name, 
       $root, 
       $port, 
       $database-id)
     let $log := setup:log(fn:concat(
       "Creating WebDAV server ",
       $server-name))
     return $config
} catch($e) {
     setup:log($e)
   }
};
declare function create-http-server(
  $config as element(configuration), 
  $group-id, 
  $server-name, 
  $root, 
  $port, 
  $module-id, 
  $database-id) 
{
try {
     let $config := admin:http-server-create(
       $config, 
       $group-id, 
       $server-name, 
       $root, 
       $port, 
       $module-id, 
       $database-id)
     let $log := setup:log(fn:concat(
       "Creating HTTP server ",
       $server-name))
     return  $config
} catch($e) {
     setup:log($e)
   }
};

app server configuration functions

The functions in this section configure the new app server. The admin:appserver-get-id function is used to get the id of the app server from the configuration. This enables the calling functions in the configure-server.xqy module to configure the newly created app server before saving the configuration to MarkLogic Server. For more details on the operation of admin:appserver-get-id and similar functions, see Creating and Configuring Objects in a Single Transaction.

The following functions set the error handler and URL rewriter for the app server. As with all other functions in this module, we first check for the presence of an object before creating it.

declare function set-error-handler(
  $config as element(configuration), 
  $group-name as xs:string, 
  $server-name as xs:string, 
  $ErrorHandler as xs:string) 
{
try {
     let $appserver-id := 
        admin:appserver-get-id(
          $config, 
          admin:group-get-id($config, $group-name),
          $server-name)
(: Get all of the existing error handlers for this server :)
     let $ExistingHandler :=
        admin:appserver-get-error-handler(
          $config, 
          $appserver-id)
(: Check to see if error handler is already set. If not, set 
   error handler :)
     let $config :=
        if ($ErrorHandler = $ExistingHandler) 
        then $config
        else
          admin:appserver-set-error-handler(
            $config, 
            $appserver-id,
            $ErrorHandler)
     let $log := setup:log(fn:concat(
       "Setting error handler ",
       $server-name))
     return $config
} catch($e) {
     setup:log($e)
   }
};
declare function set-url-rewriter(
  $config as element(configuration), 
  $group-name as xs:string, 
  $server-name as xs:string, 
  $URLrewriter as xs:string) 
{
try {
     let $appserver-id := 
        admin:appserver-get-id(
          $config, 
          admin:group-get-id($config, 
          $group-name),
          $server-name)
(: Get all of the existing URL writers for this server :)
     let $ExistingURLrewriterr :=
        admin:appserver-get-url-rewriter(
          $config, 
          $appserver-id)
(: Check to see if URL writer is already set. If not, set URL writer :)
     let $config :=
        if ($URLrewriter = $ExistingURLrewriterr) 
        then $config
        else
          admin:appserver-set-url-rewriter(
            $config, 
            $appserver-id,
            $URLrewriter)
     let $log := setup:log(fn:concat(
       "Setting URL rewriter ",
       $server-name))
     return $config
} catch($e) {
     setup:log($e)
   }
};

get-security-db function

Because there may be more than one database named 'Security,' the get-security-db function is used by the install.xqy and uninstall.xqy modules to identify the Security database used by the Admin app server.

declare function get-security-db(
  $config as element(configuration), 
  $group-name as xs:string, 
  $server-name as xs:string) 
{
try {
     let $appserver-id := admin:appserver-get-id(
       $config, 
       admin:group-get-id($config, $group-name),
       $server-name)
     return admin:appserver-get-database(
       $config, 
       $appserver-id)
} catch($e) {
     setup:log($e)
   }
};

delete-group function

The delete-group function first checks to see whether the specified groups exists. If so, it calls the admin:group-delete function to remove the group. This function is not called any function in the configure-server.xqy module, but could be used by other high-level modules to remove a group from the configuration.

declare function delete-group(
  $config as element(configuration), 
  $GroupName as xs:string) 
{
try {
     let $group := xdmp:group($GroupName)
(: Get all of the existing groups :)
     let $ExistingGroups :=
         for $id in admin:get-group-ids($config)
            return admin:group-get-name($config, $id)
(: Check to see if group exists. If so, remove the group :)
     let $config :=
        if ($GroupName = $ExistingGroups) 
        then admin:group-delete($config, $group) 
        else $config
     return $config
} catch($e) {
     setup:log($e)
   }
};

delete-server function

The delete-server function first checks to see whether the specified app server exists. If so, it calls the admin:appserver-delete function to remove the app server.

declare function delete-server(
  $config as element(configuration), 
  $ServerName as xs:string, 
  $group-name as xs:string) 
{
try {
     let $group := xdmp:group($group-name)
(: Get all of the existing servers :)
     let $ExistingServers :=
        for $id in admin:get-appserver-ids($config)
           return admin:appserver-get-name($config, $id)
     (: Check to see if server exists. If so, remove the server :)
     let $config :=
        if ($ServerName = $ExistingServers) 
        then admin:appserver-delete(
                $config, 
                admin:appserver-get-id(
                  $config, 
                  $group, 
                  $ServerName))
        else $config
     return $config
} catch($e) {
     setup:log($e)
   }
};

set-host function

The set-host function calls the admin:host-set-group function to set the host for the specified group. This function is not called any function in the configure-server.xqy module, but could be used by other high-level modules to create another configuration.

declare function set-host(
  $config as element(configuration), 
  $group-name as xs:string, 
  $host-name as xs:string) 
{
try {
     let $group := xdmp:group($group-name)
     let $host := xdmp:host($host-name)
     return admin:host-set-group(
       $config, 
       $host, 
       $group)
} catch($e) {
     setup:log($e)
   }
};

databases.xqy

The databases.xqy module defines functions that call the admin functions that create, configure, and remove the forests and databases. The functions in this module are more 'generic' than the configuration-specific functions in the configure-server.xqy module that call them. Consequently, the functions in this module could be applied to a variety of configurations.

The functions in this module are:

Each function in this module first checks for the presence of the object it is to create or remove before taking any action. This is done to enable partial changes to be made to the configuration without the entire transaction failing, as described in Making Transactions Idempotent.

Before defining the functions, we define this module's namespace as db and import the following modules:

xquery version "1.0-ml";
module namespace db = "http://marklogic.com/sampleConfig/database";
declare namespace error = "http://marklogic.com/xdmp/error";
declare namespace xdmpdb = "http://marklogic.com/xdmp/database";
import module namespace admin = "http://marklogic.com/xdmp/admin" 
    at "/MarkLogic/admin.xqy";
import module namespace setup = 
  "http://marklogic.com/sampleConfig/setup-lib"
    at "setup-library.xqy";

create-forest function

The create-forest function first checks to see whether the specified forest exists. If not, it calls the admin:forest-create function to create the new forest.

declare function create-forest(
  $config as element(configuration), 
  $NewForest as xs:string)
{
try {
(: Get all of the existing forests :)
     let $ExistingForests :=
         for $id in admin:get-forest-ids($config)
            return admin:forest-get-name($config, $id)
(: Check to see if forest already exists. If not, create new forest :)
     let $config :=
        if ($NewForest = $ExistingForests) 
        then $config
        else 
          admin:forest-create(
            $config, 
            $NewForest, 
            xdmp:host(), 
            "")
     return $config
} catch($e) {
     setup:log($e)
   }
};

create-database function

The create-forest function first checks to see whether the specified database exists. If not, it calls the admin:database-create function to create and configure the new database.

declare function create-database(
  $config as element(configuration), 
  $NewDatabase as xs:string) 
{
try {
     let $security-db := admin:database-get-id($config, "Security")
     let $schema-db :=   admin:database-get-id($config, "Schemas")
(: Get all of the existing databases :)
     let $ExistingDatabases :=
         for $id in admin:get-database-ids($config)
            return admin:database-get-name($config, $id)
(: Check to see if database already exists. If not, create new 
   database :)
     let $config :=
        if ($NewDatabase = $ExistingDatabases) 
        then $config
        else  
          admin:database-create(
            $config, 
            $NewDatabase, 
            $security-db, 
            $schema-db)
     return $config
} catch($e) {
     setup:log($e)
   }
};

database configuration functions

The functions in this section configure the new database. The admin:database-get-id function is used to get the id of the database from the configuration. This enables the calling functions in the configure-server.xqy module to attach the forests and configure the newly created database before saving the configuration to MarkLogic Server. For more details on the operation of admin:database-get-id and similar functions, see Creating and Configuring Objects in a Single Transaction.

The following functions attached the forests and add fields and indexes to the database. As with all other functions in this module, we first check for the presence of an object before creating it.

(: Attach forest to database function :)
declare function attach-forests(
  $config as element(configuration), 
  $dbname as xs:string, 
  $forest as xs:string) 
{
try {
(: Get all of the forests attached to this database :)
     let $AttachedForests :=
         admin:forest-get-name( 
           $config, 
           (admin:database-get-attached-forests(
             $config, 
             admin:database-get-id($config, $dbname)) ))
(: Check to see if forest is already attached to the database. 
   If not, attach the forest to the database :)
     let $config :=
        if ($forest = $AttachedForests) 
        then $config
        else
           admin:database-attach-forest(
              $config, 
              admin:database-get-id($config, $dbname),
              admin:forest-get-id($config, $forest) )
     return $config
} catch($e) {
     setup:log($e)
   }
};
(: Add field to database function :)
declare function add-field(
  $config as element(configuration), 
  $dbname as xs:string, 
  $field as xs:string) 
{
try {
     let $dbid := admin:database-get-id($config, $dbname)
(: Construct a specification for this field :)
     let $fieldspec :=   admin:database-field($field, fn:false())
(: Get all of the existing fields for this database :)
     let $ExistingFields :=
        fn:data(admin:database-get-fields(
           $config, 
           $dbid)/xdmpdb:field-name)
(: Check to see if field already exists for this database. 
   If not, add the field :)
     let $config :=
        if ($field = $ExistingFields) 
        then $config
        else 
           admin:database-add-field($config, $dbid, $fieldspec)
     return $config
} catch($e) {
     setup:log($e)
   }
};
(: Add include element to field function :)
declare function add-field-included(
  $config as element(configuration), 
  $dbname as xs:string, 
  $field as xs:string,
  $included as xs:string) 
{
try {
     let $dbid := admin:database-get-id(
        $config, 
        $dbname)
(: Construct a specification for this include element :)
     let $fieldspec := admin:database-included-element(
        "http://marklogic.com/wikipedia", 
        $included, 
        1.0, 
        "",
        "", 
        "")
(: Get all of the existing included elements for this field:)
     let $ExistingIncludes :=
        fn:data(admin:database-get-field-included-elements(
           $config, 
           $dbid,
           $field)/xdmpdb:localname)
(: Check to see if field already has the included elements. 
   If not, add the included elements to the field :)
     let $config :=
        if ($included = $ExistingIncludes) 
        then $config
        else 
           admin:database-add-field-included-element(
              $config, 
              $dbid, 
              $field, 
              $fieldspec)
     return $config
} catch($e) {
     setup:log($e)
   }
};
(: Add range element index to database function :)
declare function add-range-element-index(
  $config as element(configuration), 
  $dbname as xs:string, 
  $type as xs:string, 
  $namespace-uri as xs:string,
  $localname as xs:string)
{
try {
     let $dbid := admin:database-get-id(
        $config, 
        $dbname)
     (: Define a new range element index :)
     let $range-index := admin:database-range-element-index(
        $type, 
        $namespace-uri,
        $localname, 
        "http://marklogic.com/collation/", 
        fn:false() )
(: Get all of the existing range element indexes for this database :)
     let $ExistingREindexes :=
        fn:data(admin:database-get-range-element-indexes(
           $config, 
           $dbid)/xdmpdb:localname)
(: Check to see if range element index already exists for this
   database. 
        If not, add the range element index :)
     let $config :=
        if ($localname = $ExistingREindexes) 
        then $config
        else 
           admin:database-add-range-element-index(
              $config, 
              $dbid, 
              $range-index)
     return $config
} catch($e) {
     setup:log($e)
   }
};
(: Add range element attribute index to database function :)
declare function add-range-element-attribute-index(
  $config as element(configuration), 
  $dbname as xs:string, 
  $type as xs:string, 
  $parent-namespace-uri as xs:string,
  $parent-localname as xs:string,
  $namespace-uri as xs:string,
  $localname as xs:string,
  $collation as xs:string) 
{
try {
     let $dbid := admin:database-get-id(
        $config, 
        $dbname)
(: Define a new range element attribute index :)
     let $range-attribute-index := admin:database-range-element-attribute-index(
        $type, 
        $parent-namespace-uri, 
        $parent-localname,
        $namespace-uri,
        $localname, 
        $collation, 
        fn:false() )
(: Get all of the existing range element attribute indexes for 
   this database :)
     let $ExistingREAindexes :=
        fn:data(admin:database-get-range-element-attribute-indexes(
           $config, 
           $dbid)/xdmpdb:localname)
(: Check to see if range element attribute index already exists for 
   this database. If not, add the range element attribute index. :)
     let $config :=
        if ($localname = $ExistingREAindexes) 
        then $config
        else 
           admin:database-add-range-element-attribute-index(
              $config, 
              $dbid, 
              $range-attribute-index)
     return $config
} catch($e) {
     setup:log($e)
   }
};

delete-database function

The delete-database function first checks to see whether the specified database exists. If so, it calls the admin:database-delete function to remove the database.

declare function delete-database(
  $config as element(configuration), 
  $DatabaseName as xs:string) 
{
try {
     let $database := admin:database-get-id($config, $DatabaseName)
(: Get all of the existing databases :)
     let $ExistingDatabases :=
         for $id in admin:get-database-ids($config)
            return admin:database-get-name($config, $id)
(: Check to see if database exists. If so, remove the database :)
     let $config :=
        if ($DatabaseName = $ExistingDatabases) 
        then admin:database-delete(
               $config, 
               admin:database-get-id($config, $DatabaseName))
        else $config
     return $config
} catch($e) {
     setup:log($e)
   }
};

delete-forest function

The delete-forest function first checks to see whether the specified forest exists. If so, it calls the admin:forest-delete function to remove the forest.

declare function delete-forest(
  $config as element(configuration), 
  $ForestName as xs:string) 
{
try {
     let $forest := admin:forest-get-id($config, $ForestName)
(: Get all of the existing forests :)
     let $ExistingForests :=
         for $id in admin:get-forest-ids($config)
            return admin:forest-get-name($config, $id)
(: Check to see if forest exists. If so, remove the forest :)
     let $config :=
        if ($ForestName = $ExistingForests) 
        then admin:forest-delete(
               $config, 
               admin:forest-get-id($config, $ForestName),
               fn:true())
        else $config
     return $config
} catch($e) {
     setup:log($e)
   }
};

configure-security.xqy

Calls the functions in the security module to create new roles and users. Unlike the configure-server.xqy, which calls the admin functions indirectly through the groups.xqy and databases.xqy modules, this module calls the security functions directly. Consequently, the functions in this module are specific to the configuration.

The functions in this module are:

Each function in this module first checks for the presence of the object it is to create or remove before taking any action. This is done to enable partial changes to be made to the configuration without the entire transaction failing, as described in Making Transactions Idempotent.

Before defining the functions, we define this module's namespace as conf-security and import the following modules:

xquery version "1.0-ml";
module namespace conf-security=
  "http://marklogic.com/sampleConfig/conf-security";
declare namespace error="http://marklogic.com/xdmp/error";
import module namespace setup=   "http://marklogic.com/sampleConfig/setup-lib"      at "setup-library.xqy";
import module namespace sec="http://marklogic.com/xdmp/security"
    at "/MarkLogic/security.xqy";

configure-roles function

The configure-roles function first checks to see whether the specified roles exist. If not, it calls the sec:create-role functions to create the new roles.

declare function configure-roles()
{
try {
    (: Get all of the existing roles :)
    let $ExistingRoles := fn:data(/sec:role/sec:role-name)
    (: Check to see if role already exists. If not, create new role :)
    return (
      if ("Sample" = $ExistingRoles)
        then "  Sample role already exists"
        else  
          sec:create-role(
            "Sample",
            "Temporary access",
            ("filesystem-access"),
            (xdmp:permission("security", "read")),
            ("testDocument")),
      if ("Developer" = $ExistingRoles)
        then "  Developer role already exists"
        else 
          sec:create-role(
            "Developer",
            "Developer access",
            ("filesystem-access", "pipeline-execution"),
            (xdmp:permission("security", "read"), xdmp:permission("security", "update")),
            ("testDocument")),
      if ("Tester" = $ExistingRoles)
        then "  Tester role already exists"
        else 
          sec:create-role(
            "Tester",
            "Tester access",
            ("filesystem-access"),
            (),
            ("testDocument"))
    )
} catch($e) {
     setup:log($e)
   }
};

configure-users function

The configure-users function first checks to see whether the specified user exists. If not, it calls the sec:create-user function to create the new user and assign it a role created by the previous configure-roles function.

declare function configure-users()
{
try {
    (: Get all of the existing users :)
    let $ExistingUsers := fn:data(/sec:user/sec:user-name)
    (: Check to see if user already exists. If not, create new user :)
    return (
      if ("Jim" = $ExistingUsers)
        then "  User Jim already exists"
        else  
          sec:create-user(
            "Jim",
            "Jim the temp",
            "newguy",
            "Sample",
            (xdmp:permission("security", "read")),
            ("http://marklogic.com/dev_modules")) 
    )
} catch($e) {
     setup:log($e)
   }
};

remove-users function

The remove-users function first checks to see whether the specified user exists. If so, it calls the sec:remove-user function to remove the user.

declare function remove-users()
{
try {
    (: Get all of the existing users :)
    let $ExistingUsers := fn:data(/sec:user/sec:user-name)
    (: Check to see if user exists. If so, remove user :)
    return (
      if ("Jim" = $ExistingUsers)
        then sec:remove-user("Jim")
        else () 
    )
} catch($e) {
    setup:log($e)
  }
}; 

remove-roles function

The remove-roles function first checks to see whether the specified roles exist. If so, it calls the sec:remove-role functions to remove the roles.

declare function remove-roles()
{
try {
(: Get all of the existing roles :)
    let $ExistingRoles := fn:data(/sec:role/sec:role-name)
(: Check to see if role exists. If so, remove role :)
    return (
      if ("Sample" = $ExistingRoles)
        then sec:remove-role("Sample")
        else (),
      if ("Developer" = $ExistingRoles)
        then sec:remove-role("Developer")
        else (),
      if ("Tester" = $ExistingRoles)
        then sec:remove-role("Tester")
        else ()
    )
} catch($e) {
     setup:log($e)
   }
};
« Previous chapter
Next chapter »