A Dynamic Implemention of a Static Site
Implementing a WebObjects driven site looks like being an all-or-nothing project. Creating WebObjects applications, then linking to them from a static index page is clearly missing the entire point of WebObjects - make the application your site for everything. It make sense, in some ways, for a gradual implementation of a very large complex site, but not for a small scale personal site.
Various aspects of this possible implementation are frequently asked (and unanswered) questions on the WebObjects mailing lists, so I have written a sampler application that addresses this issue.
MixedSite.woa
This is a direct action based site, intended to serve files from its server's doc root with some minor substitutions.
In addition, the file direct action parses the contents of the file for two custom tags to act as includes. By this means, a static site can be maintained to include WOComponent page fragments inside static pages. This can be simply extended to pass in some form of context to modify the behaviour of those fragments - like the current path to the main page, for example.
As a minor convenience, this application will attempt to load and process /index.html from its server if called with no parameters.
To implement this as the driver application for a site, it would be convenient to use rewrite or redirect facilities so that the basic site URL will call the application. This is feasible, but could cause problems, as relative URLs might be passed to the application (for images, etc). If care is taken in implementing the rewrite, this shouldn't be a problem. Pages with relative URLs would have to be rewritten to avoid this.
Direct Actions
All of these will take their main parameter from the URL. For example, requesting the page at /index.html can be done in any of these ways:
http://www.paullynch.org/cgi-bin/WebObjects/MixedSite.woa/?index.html http://www.paullynch.org/cgi-bin/WebObjects/MixedSite.woa/file?index.html http://www.paullynch.org/cgi-bin/WebObjects/MixedSite.woa/wa/file?index.html http://www.paullynch.org/cgi-bin/WebObjects/MixedSite.woa/wa/file?/index.html http://www.paullynch.org/cgi-bin/WebObjects/MixedSite.woa/wa/file?file=index.html
It implements three direct actions:
- wo, which returns the result of pageWithName() called on the parameter passed to it (wo=);
- file, which returns the file whose name was passed as a parameter (file=);
- url, which returns the URL whose name was passed as a parameter (url=).
The woAction() method could be modified to see if the component name accepts a file path, and if it does to set it.
Custom Tags
There are two of these:
- PLINCLUDE: Take an attribute file=, which specifies the file to include relative to the document root.
- PLURL: Takes an attribute url=, which specifies the URL to embed.
Examples:
<PLINCLUDE file=index.html.en> <PLURL url=http://www.paullynch.org/>
Implementation
Making the entry page a direct action
When a WebObjects application is entered, it normally returns the WOComponent called "Main". This behaviour is programmed in, but can be changed in a number of ways. If you are developing a direct action based application, you have to change the default resource handler to be the da resource handler, and then implement the defaultAction() method. One if provided for you, that calls up the Main component.
To change this, put the following code in your Application class constructor:
setDefaultRequestHandler(requestHandlerForKey(directActionRequestHandlerKey()));
And change defaultAction() to this, where fileAction() is a method already implemented:
public WOActionResults defaultAction() { NSLog.debug.appendln("defaultAction"); return fileAction(); }
This means that the application will call the fileAction() method without having to provide the normal URL for this (/wa/file).
Implementing fileAction()
This is a direct action that creates a WOResponse based on a parameter passed in the URL, and returns the contents of the file name as the response contents.
public WOActionResults fileAction() { String content = (String)request().formValueForKey("file"); if (content == null) content = (String)request().formValueForKey("WOIsmapCoords"); if (content == null) content = "index.html"; WOResponse nextPage = new WOResponse(); content = PLUtilities.getFileString(NSPathUtilities.stringByAppendingPathComponent( docRoot(), content)); // should filter content for custom tags first content = plInclude(content); content = plUrl(content); nextPage.appendContentString(content); return nextPage; }
If the filename is passed in as a parameter (file=somename) according to standard URL construction rules, then the first line of this method will locate it. If, however, it is passed in as the remainder of the line, it will be put into the formValues dictionary with a key of WOIsmapCoords, which we use in the second line of the method. If no parameter is passed, it assumes "index.html". The rest of fileAction() creates the WOResponse, retrieves the file contents using the PLUtiltities.getFileString() call, and filters the returned contents using plInclude() and plUrl(), before putting the new string into the response using appendContentString(); there is an alternative method, appendHTMLConentString() that (contrary to the name) escapes any html strings in the value passed to it. Using setContent(NSData) would be an option if you didn't want to process the string first.
Note the call to docroot(). This has to be hard coded, as WebObjects has no way of knowing where the document root of the web server you are accessing it through is located. This is an obvious place that customisation is likely to be necessary. In a live application, I would load this in from a configuration file, the name of which is given to the application either from defaults/properties, or from the command line at launch.getFileString()
This is just a chunk of pure Java that I downloaded from the web somewhere.
plInclude() and plUrl()
These are responsible for parsing the file contents for the PLINCLUDE and PLURL tags. They use a couple of utility classes that I wrote a long time ago for parsing html. I don't make any claims that they are very good, but they work well enough that I have been reusing them for a couple of years.
Download source code for this project.