Creating a Virtual Router
Before restarting the server, the next stage involves setting up a virtual router. This takes advantage of the XQuery URI Rewrite facility in order to create specific XQuery scripts that redirect content to the appropriate services.
Before setting up this facility, it's worth learning how eXist performs URI redirects. The file $exist/webapp/WEB-INF/web.xml declares the various services. The goal here is to create a new /xrx/ service in a manner similar to the /rest/, /webdav/, /xml-rpc/, /atom/, and other services that eXist exposes. Specifically, look for the following:
This establishes a handle for the service itself and associates that handle (RedirectorTest) with a given dispatcher (in this case, $exist/webapp/redirector/dispatch.xql). To create a link to a different dispatcher (recommended), you need to create a new entry. For instance, the XRX application uses the following:
You can have multiple such dispatchers, so long as each has a unique servlet-name element. The above code will look in $exist/webapp/xrx for the dispatch.xql script.
Farther down in the same file, you also need to associate this service with a URL mapping. This is done with the servlet-mapping declaration. You can create a new servlet mapping by providing the servlet-name and the url-pattern that will be matched:
What this means is that whenever an expression of the form http://localhost:8080/exist/xrx/myURL is encountered, the URL information (and additional session state information) is then passed to the XRXDispatcher, which in turn calls $exist/webapp/xrx/dispatch.xql.
The dispatcher.xql is an XQuery that returns an XML structure back to the servlet engine. For the XRX dispatcher, this script looked like Listing 5.
The request: namespace accesses items from the servlet's request environment up to and including incoming data. The specific information passed back to the redirector servlet is contained in the entry. If the logout parameter is passed and is not empty (i.e., the URL has the form /xrx?logout=true), then the application downloads the default eXist XML page. Otherwise, the routine passes in all of the server HTTP headers as parameters to a second XQuery script at $exist/webapp/xrx/virtual.xq.
Note that you can use additional information from the request:get-path-info() function to create subservices. For instance, suppose that you wanted to set up a specific redirect path for administrative services. You could do this by writing the if statement in Listing 5 as shown in Listing 6.
This way, http://localhost:8080/exist/xrx/admin/users would retrieve the sub-path /users as a parameter and route content to the $exist/webapp/xrx/admin.xql XQuery script.
|Author's Note: The /exist/ part is part of the underlying servlet engine mapping, which means that if you were to replace http://localhost:8080/exist/ with http://myapp.com/ in your Jetty or Tomcat, it would not effect the XQuery content (e.g., http://myapp.com/xrx/admin/users would work in exactly the same way as http://localhost:8080/exist/xrx/admin/users.|
Now, in the eXist implementation, the original assumption (at least in the 1.2.x implementation) was that any such services would be implemented outside the database, though these services certainly would have access to content within the database. The problem I have with this approach is that any time I add a new "virtual collection" into the mix I have to stop and restart the database. What I wanted to do instead was to have the XQuery scripts that supported these collections reside within the database itself, which means that changes to the scripts would immediately get reflected in performance without that restart.
This was the reason for the virtual.xq script, which is invoked from the dispatcher in Listing 6. The virtual.xq script performs this task by invoking a router script (dispatch.xq) within the database itself. The eXist util:eval() function also makes available any variables that were defined within the same script context, which in this case means that the <parameters/> XML document is passed directly to dispatch.xq (see Listing 7).
This routine uses the principle of sequences to add additional information to the parametersthe unqualified path, query string parameters. The following code constructs a sequence from distinct pieces, which is the bulk of what goes on in this script:
let $a = ()
let $a := ($a, $b)
In this case, it is used to expand upon the parameter list.
Think of these steps as bootstrapping the dispatch from the context of the eXist webapp to the context of the database.