SimpleXist II

A couple of days ago I did a post on a simple eXist setup – lazily called SimpleXist . As I left it, it was a running system but there were a few shortcomings and complaints when it runs :- a) No JSP support,  b) No log4j config, oh and the minor matter  of no eXist client to talk to the database! So it’s time for a bit more configuration and then we’ll take it for a spin with a couple of simple examples.

As per last time if you want the version described here, you can just download it.

Errors & Warnings

This is what I get currently from the system on startup :

macbook:Test tingenek$ java -jar start.jar
2012-01-01 17:35:08.111:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
2012-01-01 17:35:08.237:INFO::jetty-6.1.26
2012-01-01 17:35:08.503:INFO::NO JSP Support for,
did not find org.apache.jasper.servlet.JspServlet
log4j:WARN No such property [append] in org.apache.log4j.ConsoleAppender.
2012-01-01 17:35:10.531:INFO::Started SelectChannelConnector@0.0.0.0:8080

Now the JSP issue is easy, you can either add in the jsp-2.1 folder into Jetty /lib, or just ignore it – it does no harm. The logging just needs a bit more configuration.

The eXist software uses the log4j system to write sophisticated logs for various modules. In our case what we wanted was just simple logging to the console with everything else. It’s easy to setup. First create in /ROOT/WEB-INF a folder called /classes. In that folder put this file, log4j.xml. All it does is create an appender to pipe everything to the console. Note the use of the “priority” setting in the <root> element. This controls how much logging eXist does, at the moment it’s just errors but you can dial it up to “info”, “warn” or even “debug” if you really want to see everything as it happens.

eXist Client

It may not be obvious, but the eXist client is run from the start.jar in the .war file /lib directory. So, first step is to copy it, renaming it to client.jar and add it to SimpleXist. You should now have start.jar and client.jar in the same folder. If you try and run it, you’ll find it complains about not being able to find eXist root and bombs. What’s needed is a small shell file to fire it up, so make a file called called client.sh, and paste the following in:

#!/bin/bash
java -Dexist.home=webapps/ROOT/WEB-INF -jar client.jar client

Now if you run this, it’ll complain about not finding lib files in various folders; that’s because we have a flat lib, but it’ll work and you’ll get the eXist client. Make sure you alter the client URL as we’ve used localhost:8080/xmlrpc rather than /exist/xmlrpc. Also, default .war database install doesn’t have an admin password – remember to set one once you’re in!

Note. I might get around to repackaging the client jar at some point so it doesn’t complain, but at the moment it’s not a priority. 

Before we begin – housekeeping.

As with everything, it’s always good to have some sort of system in place; everyone’s happier when the expected occurs. Now, this is just the way I’ve set this up, feel free to do something different, the key is predictability. So, for SimpleXist there is:
  • A folder off from /ROOT for each project.
  • A /resources folder off /ROOT holds common files, like XSLTForms, TinyMCE
  • A /resources folder in each project holds resources for that project i.e. /css /scripts /images
  • A folder called /styles holds any xslt files.
  • The XQuery files are in each project root.
  • In the eXist database a collection with the same name as the project off from a /projects collection.
Idea! Because we have ROOT and local resources we can have things like a generic /resources/css/site.css in our header to set up things generally, followed by a project specific resources/css/project.css over-riding specific settings.

Getting by without pipelining.

In anything but the most trivial system, we’ll need some sort of framing for the site: the header, footer, navigation etc that we don’t want to repeat per page. Now a common route is to use a pipeline with the XML being turned into HTML through Cocoon,XProc, URLRewriting etc. In SimpleXist this isn’t possible as we’ve got none of those things – though, we could have, but choose not to is more accurate. To get around that we’ll use the excellent transform module in eXist to convert our XQuery into HTML for us at the end of each XQuery using an XSL transform, like so:

First, create a new folder under /ROOT called project1. Now create an index.xql file with the following content:

xquery version "1.0";
declare namespace transform="http://exist-db.org/xquery/transform";
declare option exist:serialize "method=xhtml media-type=text/html indent=yes";
let $input :=
<document>
<content>
<h2><a name='h1'>Test</a></h2>
<p>Hello from XSLT</p>
</content> 
</document>
return
transform:transform($input, "styles/style.xsl", ())

This XQuery sets up a simple bit of XML with a mime-type of html and then calls a transform against it from our /styles folder, returning the results. To get this to work, create a /styles folder in /resources. In /styles put the following style.xsl file. There is also a tiny amount of css so you’ll also need /css and site.css. If you have a look at the XSL file, you’ll see that it simply pulls the document/content element out of the XQuery into the appropriate place in a simple piece of HTML. Now a quick look at localhost:8080/project1 should give you a plain’ish piece of html.

Now this might seem trivial but it just requires a bit more structure and CSS and you’ve the basics of a web site. Even better, if you look at the XQuery, you’ll see that that bit of XML could just as easily come from the eXist db. In fact, you could run a whole site from this bit of code, just by giving it the id of the bit of xml you want to drop in. We’ve got a bit of a WordPress vibe starting 🙂

Using the database.

Fire up your eXist client (you created a password for admin didn’t you?) and go to File->Create Collection. Create a /projects collection and inside that a /project1 collection. In /project1 use File->Create Blank File, call it 1.xml and inside it copy :

<document>
<content>
<h2><a name='h1'>Page 1</a></h2>
<p>Hello from page 1</p>
</content> 
</document>

Make a couple more, excitingly called 2.xml and 3.xml. Now alter the index.xql file to pull the content from the database:

xquery version "1.0";
declare namespace transform="http://exist-db.org/xquery/transform";
declare option exist:serialize "method=xhtml media-type=text/html indent=yes"; 
let $data_collection := '/db/projects/project1/'
let $q := request:get-parameter('q', '1')
let $file := concat($data_collection,$q,'.xml')  
let $error :=
<document>
<content>
<h2><a name='h1'>Oops</a></h2>
<p>I can't find {$file} in the database</p>
</content> 
</document> 
let $input := if(doc-available($file) = true()) 
then (doc($file)) else $error
return
transform:transform($input, "styles/style.xsl", ())

What happens here is that we look for a request parameter ‘q’. If it doesn’t exist, we set it to 1. Next we make up a path in the collection to from the root and the parameter to get a file path like /db/projects/project1/1.xml. Next, we check a document exists for that path, if it doesn’t we use an error doc, otherwise we fetch it. Lastly it gets transformed and returned.

If all goes well, you can now use localhost:8080/project1/index.xql?q=1 or the rather terser /project1/?q=1 to get document 1.xml etc.

This is one of the things I like about the XRX system: simple, clean XML  data and small amounts of XQuery code give you (with a bit more work) the tools to put a site together!

What’s missing now is the XForms side. Next time..

Advertisements