Developers > Programming > Advanced Web Application Programming

Advanced Web Application Programming

Most DCDs will have many additional methods and routines; the read(), write(), and ioctl() methods are simply the ones that ExSite uses to interface to the DCD. In some cases, you may need to split your DCD into multiple Perl packages. The additional packages can also be set up as DCDs, with their own entry points (if it makes sense to do so), or they can be stand-alone packages only invoked by the single DCD.

Example: a registration or purchasing system may have a complicated series of tools for the end-user's front-end/e-commerce system and the administrator's back-end/reporting system. Although these can be placed all together into a single monolithic package, it may make sense to keep them separate, especially if the only interaction between the sub-systems is through the database. If these functions are placed together in a single package, then you will have a single write() method that can generate both end-user and administrator content, which means you will have to be more careful about security, and make more effort to keep the code easy to read. If you broke the end-user and administrator toolkits into separate DCDs, security and readability are both enhanced. In summary, a single web application does not imply a single DCD module.

In some cases it may be necessary or desirable to create libraries or modules that are not DCDs themselves. For instance, in the above example, if there is a set of functions required by both the front and back ends, it would be best to place them in a package that is loaded by both DCDs. But if this package is placed in the DCD directory, ExSite will attempt to load it up as a driver. Although ExSite tries to handle non-conforming packages gracefully, it still results in an unnecessary performance hit. In this case, supporting packages can be placed in a subdirectory of the DCD directory. ExSite will ignore them when scanning its drivers, but the drivers themselves can load the packages as needed:

cgi-bin/Modules/Reg.pm         <-- user front-end DCD to registration system
cgi-bin/Modules/RegAdmin.pm <-- administrator back-end DCD
cgi-bin/Modules/Reg/RegLib.pm <-- shared registration system toolkit (non-DCD)

To load the RegLib.pm toolkit, the DCDs would simply include the line use Modules::Reg::RegLib.pm; at the top of the DCD modules.

Database-driven Applications

Database-driven web applications can be encoded in a DCD, using all the same programming methods described in standard CGI and web database programming guides. The most important difference is that the DCD output is returned to the caller in a string, rather than printed to stdout.

ExSite has its own database interfacing tools, which have many features that may be useful to web database programmers. See the ((Introduction to the ExSite Kernel|kernel programming docs)) for more information on these tools. The ExSite database API is only required when interacting with ExSite's default database, which is used for basic content management. This database can be extended to incorporate the tables/functions required by the local web applications, or a second ExSite-compatible database can be created and connected to separately from the content database.

If using the default database, there is no need to establish a database connection, since ExSite will already have one open. You can find this default database handle in $share{DB}.

If the developer does not want to work with the ExSite database tools at all, the DCD can perform its own database connections and interactions in any way that it chooses.

Web Applications Written in Languages Other than Perl

Web applications written in other languages will have relatively simple Perl DCDs that simply act as a call translation layer to interface to the rest of the application.

In the simplest case, if your web applet is packaged in an executable format, you can simply spawn it as a separate process. For example:

sub write {
my ($this,$options) = @_;
# myapplet can get the HTTP request data from the environment
return `myapplet.exe`;
}

Although nice and simple, this solution is not especially high-performance, since you pay for the overhead of launching a new process and loading and executing another program. (This is no big deal in a basic CGI setup, since you've already paid this price once to start up Perl, which may be substantially more costly than your binary.) This general technique is extensible through inter-process communication methods, outlined below.

For better performance, Perl has methods for interfacing directly to C-compatible binary libraries, so that it runs entirely within the same process. For more information, see the perlxs tools (do "man perlxs" or "man perlxstut" for more info).

Perl also has a variety of language interfacing tools for various lanaguages, including C++, Java, Python, and more. The Inline tools (see Cpan.org) allow you to inline the other languages directly into your ExSite drivers, which can greatly ease the job of "gluing" your driver to a backend that is native to another language.

Inter-process Communication

If your applets run externally and command line switches are insufficient to completely determine the applet's output, then some more advanced IPC tricks will have to be used.

For example, some external applications expect input (eg. commands) to come through stdin. In this case, you will have to open a bidirectional pipe to the application so that you can send the input and capture its stdout. See the perlipc manpage for more on this. Example:

use FileHandle;
use IPC::Open2;
$pid = open2(*myapp_read, *myapp_write, "myapp.exe");
print myapp_write "commands\n";
$output = <myapp_read>;

Examples of this type of IPC can be found in the ExSite::Image module, which uses the external convert program to modify images.

The unidirectional pipe (eg. open MYAPP, "|myapp.exe";) will not generally work for inlining HTML content into a page, because the output will go to stdout, and will not be captured for proper insertion into the web page templates. (It may also mess up the HTTP headers of the reply, if those haven't been output yet.) However, note that secondary content such as images are fetched in separate requests that require no templating. In these cases it is safer to use unidirectional pipes that simply dump their results to stdout.

There are other tricks that can be used for passing input and output between two separated processes, such as signals, fifos (named pipes), shared memory, and plain ol' disk files.

For high performance external applets that avoid process creation overhead (which can be costly for large programs), you can consider client-server models of construction. In essence, your applet runs continuously, and ExSite simply connects to it, sends a reqeust, and receives a reply. This can be done using sockets if you wish to follow conventional client-server methods, but if full networking functionality is not necessary, you can also use shared memory or disk files for data communication, and signals to alert your server applet that a request is being made.

AJAX/DHTML

AJAX (asynchronous javascript and XML) and DHTML (dynamic HTML) are techniques for altering/updating page content without going through a full page reload. Essentially, content is fetched in the background and injected into the current page, possible replacing a block of content that was there previously. These techniques tend to improve the responsiveness of the page from the user's point of view, and also reduce server load, since only a fraction of the page needs to be generated at a time.

ExSite supports using AJAX/DHTML methods for dynamically fetching and refreshing DCD content that is embedded into a page. If you use the link() method from ExSite::BaseDCD to generate all internal links back to your DCD, then you do not need to know anything about AJAX, DHTML, CSS, or Javascript to implement these features. AJAX functionality is invoked automatically by adding additional ampersand characters to the DCD content tags, as follows:

Direct substitution (single ampersand):

<!--&Module()-->

This is the conventional syntax for invoking a DCD module with no AJAX/DHTML. The content is fetched from the DCD, and inserted directly into the page at the time the page is being built. Recursive links to the DCD are normal URLs that regenerate the entire page. This method has the advantage of accessibility (it works on all browsers, and without Javascript), and is search-engine friendly.

Indirect AJAX substitution (double ampersand):

<!--&&Module()-->

Instead of inserting the DCD content directly into the page, this method inserts some Javascript that will fetch the DCD content in a separate HTTP request. Recursive links to the DCD are still normal URLs that will regenerate the entire page. This method is best suited for instances where you have a page that is published to a static file (especially index.html, which must be static), but certain module(s) need to remain fully dynamic (such as breaking news and announcements). Note that search engines will not be able to see the DCD content, as they generally ignore javascript.

Double-indirect AJAX substitution (triple ampersand):

<!--&&&Module()-->

The initial substutition is made using AJAX/DHTML, as in the previous case. However, all recursive links to the DCD are also handled using AJAX, so that the DCD is refreshed "in place" without a full page reload. This has the benefit of faster, smoother content updates, giving a more responsive user experience. However, search engines cannot see the DCD content, and furthermore, the non-default state of the page cannot be bookmarked.

AJAX programming

Normally, you do not have to do anything to make use of the above AJAX methods. If you follow standard ExSite programming conventions, ExSite can manage all of the AJAX protocol automatically.

The most important convention to follow is in the generation of recursive links. Use the link() method provided in the BaseDCD.pm module that you should be inheriting from. Example:

# generate a reply link 
my $reply_url = $this->link(action=>"reply");
return "<a href=\"$reply_url\">Reply</a>\n";

The link() method will automatically build a conventional URL or a Javascript call depending on what AJAX mode the plug-in is running in.

The AJAX calls invoke a server-side program called dcd.cgi which executes the plug-in directly, and returns the bare content with no template wrapper. Inspect that code for details on this procedure.

Under the AJAX modes described above, the entire plug-in block can be refreshed using AJAX. If you want to refresh only a sub-block using AJAX methods, you can follow this template:

# ensure we are running in AJAX mode
if ($share{ajax_target}) {
# set your AJAX target element
# (this is the DOM ID of the element you intend to modify via AJAX):
my $target_id = "DOM_ID";

# set the plain URL to obtain the new content from the plug-in.
# If we are running in AJAX mode, this should give us a dcd.cgi URL.
my $plain_url = ExSite::Misc::relink(%options);

# the Javascript to make the AJAX substitution
# (ensure that you are also loading /_ExSite/js/httprequest.js)
my $ajax_js = "subRequestData('$target_id','$url')";

# you can use the following value in an HTML anchor's HREF attribute
my $ajax_url = "javascript:$ajax_js";

# alternatively, you can build a complete anchor tag:
my $ajax_anchor = "<a href=\"#\" onclick=\"$ajax_js\">Click here</a>";
}

Publishing

DCDs may define their own publish methods. This is particularly useful if the web application uploads or creates content in the form of auxiliary files such as images or documents. Publishing these files to disk will usually result in higher webserving performance than retrieving them from a database. If there is a significant performance hit in generating the DCD's normal text data, there may also be an advantage to pre-publishing some text elements so they do not have to be completely regenerated on each page view. It is entirely up to the individual web application to determine whether any of its content may benefit from being prepublished in this way. It is also up to the DCD to determine how to find its published files and whether or not they have been published or must be generated from scratch.

To configure your DCD for publishing, create an ioctl request, Publish, which returns a code reference to your internal publish method. For example:

sub ioctl {
my $this = shift; # DCD object
shift; # ioctl request in $_
if (/Publish/)
return \&my_publisher;
}
}

sub my_publisher {
# code to publish this DCD's files
# ...
}

Although you could invoke your publishing method directly from your control panel or write methods, in practice this will fail under most webserving configurations. That's because publishing requires special priveleges to write files, which normal webserver processes do not have for security reasons. ExSite deals with this by restricting all file writing operations to a special publishing program (publish.cgi), and gives this one program sufficient priveleges to write to files. Only publish.cgi is able to invoke your DCD's publish method with sufficient privelges to write to files, when invoked from over the web.

By convention, DCD content should be published to _Modules/ModuleName/.... As of version 3.2, ExSite does not enforce this, so in principle you can publish files anywhere on the system that you have write access to. Obviously you should be extremely careful about publishing elsewhere, to avoid overwriting files that do not originate from your DCD.

To invoke your DCD's publisher over the web, use the following URL:

/cgi-bin/publish.cgi?module=MyDCD

If you require finer-grained control over your publishing than a simple catch-all publish URL that publishes the entire DCD, then your publish method must be configured to look for additional inputs in the query string of publish.cgi, and interpret/validate them appropriately. For instance, if you want to publish only the photos for a certain article in your News module, then you could use a URL like this:

/cgi-bin/publish.cgi?module=News&article=1234

Your News DCD's publish method is responsible for finding and reading the article parameter, and knowing what to do with it (including authorizing the user to perform this action).

ExSite's generic publish tools will only include links to publish the whole DCD. If you need more specialized publishing links, you must include them in your control panel output.

Unpublishing

Unpublishing consists of removing files that you had previously published. It normally occurs when records are deleted from your DCD's data. If any of those deletions involve files that were published to disk, then it is a good idea to remove those published files to keep things tidy.

Unpublishing works in much the same manner as publishing. Write your unpublish method, which performs whatever file deletions are appropriate (be careful!), and create an ioctl("Unpublish") request that returns a reference to this method. Unpublishing occurs via a url such as this:

/cgi-bin/publish.cgi?-module=MyDCD

(Note the - symbol in front of "module", indicating that publish.cgi should be removing files instead of creating them.) If you have not defined an Unpublish ioctl request, this URL will fail with an error message.

Just as with normal publishing, you can include additional parameters if your DCD's unpublish method knows how to interpret them.

Searching

Plug-ins are free to implement their own private search tools by defining an internal search method and form. The plug-in can use its own conventions for the search form and reporting style. From the point of view of ExSite, a private search tool is just like any other DCD output. This may be easier in some cases from a programming perspective, but it also means that you could have numerous different search tools on your site, depending on the specific type of content being searched form.

Alternatively, you may wish to make your plug-in's content available to generic system searches, ie. a simple keyword search on a site-wide search form. Then you could search for your plug-in's special content using the same search tool that is used to searching the rest of the website.

ExSite's search system indexes text on the default view of each page. It does not follow links deeper into a particular plug-in to find new content. You only need to concern yourself with indexing "deep" plug-in content if the default front-page view of the plug-in content is insufficient.

Instructions for tying in to the Search system are provided in the ExSite Search Guide.

Topics