Plands.com Logo  Business Internet Services 
Main Menu
Home
Planetary CRM
  Benefits
  Features
  Demo
E-Mail Services
  E-mail Hosting
  Newsletters
  Discussion Lists
Web Hosting
Server Co-location
and Administration
Web Applications
Technical Support
Contact Us
DXP Samples
 
DxpHome  ||   Samples  ||   Details  ||   Docs  ||   Trees  ||   xml-xsl  ||   Links

Details

The dxp system is a framework for building web applications. It compiles XML source pages with special tags into server side objects that respond to http requests. It is normally used to interact with database tables and dynamically create html content on the fly. Simple and complex web applications can be constructed from a collection of dxp pages.

Pages are controlled by a DxpServlet that runs in a compatible servlet runner like Tomcat or Resin. Servlet Runners are often paried with Apache through a runtime module. The diagram below shows an Apache/Resin combination.

In a typical setup at plands.com each Virtual Host contains a separate application zone with it's own set of active pages and default services. Classes in one zone can be reloaded without affecting other Virtual Hosts. Connections to the appropriate database tables are allocated in advance.

Passing a reaquest to a .dxp page

Page requests with the file extension .dxp are passed by Apache to mod_caucho which forwards them to the correct Servlet Runner and the DxpServlet running in the correct Virtual Host. The DxpServlet will load pages from it's cache of precompiled pages or compile them from source and put them back into the cache if not found or out of date.

public DxpPage getPage(String key) throws Exception
{
    DxpPage page = null;
    String filename = docroot + key;
        
    File file = new File(filename);
    if(!file.canRead())     // if source file can't be found
    {
        cache.remove(key);  // remove any page cached by this name
        return null;        // return null, servlet will tell user it was not found
    }
        
    page = (DxpPage) cache.get(key); // look for page in cache, compare date with src file
    if(page==null || file.lastModified() > page.getModified()) 
    {
        page = createPage(filename);
        if(page != null && page.canCache())
        { 
            page.refresh(System.currentTimeMillis() + cache.getRefreshInterval());    
            cache.store(key, page);
        }    
    }
    return page;
}

Strcuture of a compiled page

DxpPages are written as XML source files and parsed with a SAX parser. As the parser reads a .dxp file it uses the DxpNodeLinker to create a set of active and passive tags and link them together into a tree of DxpNodes. Each node may have a link to it's immediate parent, first child and first sibling. The top node parent (root node) is the DxpPage itself. All the other nodes are it's children.

If you looked at the dxp source code for any page in the Sample section you will notice that some active tags begin with the prefix <dxp: >. The prefix tells the node linker to create an active node. The prefix also tells the linker where to find the Java class files to create the node using the xmlns: attribute at the top of the page.

<?xml version="1.0"?>
<dxp:DxpPage xmlns:dxp="dxp.plands.com" contentType="text/html" staticFile="index.html" buffsize="6000">
<dxp:ReadBytes filename="header-nocache.txt"/>
<dxp:ReadBytes filename="index-content.txt"/>
<dxp:ReadBytes filename="footer0.txt"/> 
</dxp:DxpPage>

The xmlns attribute refers to a namespace dxp.plands.com which points to this website but can also be reversed to create the package name com.plands.dxp. The default dxp classes reside in this package.

Tags that are not associated with a prefix and namespace are considered passive. This includes html tags, whitespace and text outside active tags. All passive char data that occurs between any two active tags is stored in a single <dxp:Cdata> node. This is automatically created by the linker. Merging passive tags and character data into a single node greatly reduces the number of active objects, lowers the memory footprint and speeds execution.

Extending dxp with your own tags

Multiple namespaces can exist on the same page so you can declare your own namespace to add your own custom tags that load from your packages. For example, the prefix xyz.yourdomain.com will be mapped to the package com.yourdomain.xyz. Custom tags need to subclass com.plands.DxpNode or one of it's decendants so they can be linked into the tree.

// YourNode.java
package com.yourdomain.xyz;

// required by all subclasses of DxpNode
import com.plands.dxp.DxpNode;
import com.plands.dxp.DxpRequest;
import com.plands.dxp.DxpException;

// required by DxpNodes
import java.io.*;
import org.xml.sax.Attributes;

public class YourNode extends DxpNode {

/** This is called by the node linker to construct any DxpNode before intializing it.*/
public YourNode(){};

/** Supply an init() method. */
public void init(int ID, Attributes atts) throws Exception
{
...
}

/** Implement your own exec() method to do work.*/
public void exec(DxpRequest req, OutputStream out) throws Exception
{
}

// You can override the default methods in DxpNode or it's subclasses now.

} // end class YourNode

Executing a page - a tree of DxpNodes

When an http request is sent to a DxpPage the request is turned into a DxpRequest and passed to the root node (the DxpPage node). The default execution path assumes that child nodes perform a service for their parents so it passes the request to innermost child nodes first, then their siblings and then back up to the parent.

public static void execChildren(DxpRequest req, OutputStream out, DxpNode caller) throws Exception
{
    DxpNode node = caller.child; // first child of the parent node
    while(node != null)
    {           
        if(node.child != null && node.canPass())
        {    
            node = node.child;      // Loop down to last child on branch.
            continue;               // Conditional tags may not allow a request to pass
        }
        else                        // last child on branch reached        
            node.exec(req, out);         
        
        if(node.sibling != null)    // if this child has a sibling
        {  
            node = node.sibling;    // move to sibling
            continue;               // follow branch down to it's last child
        }
        else                        // no more siblings left on this level
        {
            node = node.parent;     // move back up to parent
            if(node == caller)      // if this is oringinal parent
                break;              // break out of the loop rather than repeating it

            node.exec(req, out);    // exec current parent using any values stored in req by children
            node = node.sibling;    // move to sibling of parent - if any
        }                           
    }
}

Controlling execution

Many nodes override the default execution behavior and control their own children. If, ElseIf nodes are a good example and test a simple expression with parameter values before passing execution to child nodes they enclose.

<dxp:If test="records != null">
  <table border="1">
   <tr bgcolor="#CCCCFF">
    <th>No.</th>
    <th>Username</th>
    <th>Beverage</th><th>Cost</th>
   </tr>
  <dxp:LoopSet matrix="records">
   <tr>
    <td><dxp:RowNum /></td>
    <td><dxp:Index value="0"/></td>
    <td><dxp:Index value="1"/></td>
    <td><dxp:Index value="2"/></td>
   </tr>
  </dxp:LoopSet>
  </table>
</dxp:If>
<dxp:Else>
  <h3>No records found for <dxp:Rp name="person"/></h3>
</dxp:Else>
You can see a number of If, ElseIf, Else examples in the Sample section. Complex, asymetric decision trees can be built using Select, Case, Branch, Leaf as shown in the Trees section.

Database results can be written out in tables using LoopSet and LoopSetIterator tags to format each row of data. Hyperlinks can be created dynamically from tags and result sets. All this can be done on the same page without resorting to complex stylesheet processing on the server. Execution order is top down and sequential so it's easy to follow.

XSL Stylesheets are good at rendering XML tags and if you want to deliver straight XML you can use LoopSetX to format database results directly into XML and tell the browser to download an XSL stylesheet to display them. On the other hand, controlling program execution through stylesheets can be very confusing. Stylesheet processing is recursive and does not necessarily follow the top to bottom sequence you expect to see. It's easier to control execution on a dxp page and then format the result for display with a stylesheet when required. The xml-xsl section has a couple of examples. All modern browsers support XSL stylesheets so why do this on the server?

Processing results

When a node executes it can write data to an output buffer and/or it can store a result back in the request under a new parameter name. Subsequent nodes that know the parameter name can get these values when the request is passed to them and do further processing. Simple text or complex Java objects can be produced, stored and passed down the execution tree in this manner. For example, a database result is normally stored for another tag to process...

 <dxp:DbStmt action="query" dbcon="member" store="record">
  <dxp:SqlStmt>
   <dxp:Cp name="sql">select from member where lastname='?' and company='?'</dxp:Cp>
   <dxp:SqlParam name="lastname" type="CHAR" require="true"/>
   <dxp:SqlParam name="company" type="CHAR" require="true"/>
  </dxp:SqlStmt>
</dxp:DbStmt>
Eventually something must be written to the output buffer that is sent back the client when the page is done executing but nodes can also write their output to files or database tables.

Because DxpNodes store temorary results back in the request rather than storing them internally a single node tree can handle multiple requests at the same time. This is similar to a properly designed servlet and is usually referred to as being 'thread safe'. If the result was stored in the node itself the page could only handle one request at a time and would not be thread safe.

This behavior highlights a major difference between the dxp system and some other XML based systems that rely on DOM (Document Object Model) trees. A common practice is to create a new tree of nodes for each request and pass the tree to a server side stylesheet processor to format the result. This can be a very expensive operation when page content chages with every request. It still has a place when the content does not change very often and can be cached. Now that most browsers support XSL processing, there are fewer reasons to do this on the server.

A typical DxpPage is not expensive because it uses the same page for all requests and formats the result itself or instructs the browser to download the appropriate stylesheet and do it's own formatting. Some pages can become expensive over time. A page that queries a database and returns a large result set for each request will create a lot of Java objects. Eventually they will use up the memory allocated to the JVM and the garbage collector will kick in to reclaim memory from objects that are no longer used. This will cause a brief pause in the Servlet Runner and the application. If a lot of pages have been removed form the cache due to non-use and suddenly need to be recompiled this can also cause a pause.

These effects can be minimized by using dxp to write static pages that are updated at regular intervals to reflect new data. Static files can be delivered by Apache directly from your disk cache.

<dxp:DxpPage xmlns:dxp="dxp.plands.com" contentType="text/html" staticFile="mydata.html">
...
</dxp:DxpPage>

The TimerServlet can be used to call pages at regular intervals to refresh their static files with new results. Here's a sample task file read by the TimerServlet...

<?xml version="1.0"?>

<xtr:RootMap xmlns:xtr="xtr.plands.com">
 <xtr:Cp store="debug" value="true"/>
 
 <xtr:InitTask name="dxptask1" classname="com.plands.net.UrlTimerTask">
  
  <!-- this task runs on the hour beginning at 1 AM and repeats every hour -->
  <xtr:Cp name="start" value="01:00:00"/>
  <xtr:Cp name="period" value="1h"/>
 
  <!-- call this page to write a new static copy every hour -->
  <xtr:ParameterGroup name="urlinfo">
   <xtr:Cp name="url">http://dxp.plands.com/sample/index.dxp</xtr:Cp>
  </xtr:ParameterGroup> 
  
  <!-- this page will write db results for this person on the hour -->
  <xtr:ParameterGroup name="urlinfo">
   <xtr:Cp name="url">http://dxp.plands.com/sample/dbtofile.dxp</xtr:Cp>
   <xtr:Cp name="param">person=mark</xtr:Cp>
  </xtr:ParameterGroup> 
 
 </xtr:InitTask> 

 <xtr:InitTask name="dxptask2" classname="com.plands.net.UrlTimerTask">

  <!-- this runs 30 min after task1 and repeats every hour -->
  <xtr:Cp name="start" value="01:30:00"/>
  <xtr:Cp name="period" value="1h"/>
  
  <!-- this page will rewrite the db results using a new person on the half-hour -->
  <xtr:ParameterGroup name="urlinfo">
   <xtr:Cp name="url">http://dxp.plands.com/sample/dbtofile.dxp</xtr:Cp>
   <xtr:Cp name="param">person=jakob</xtr:Cp>
  </xtr:ParameterGroup> 
 
 </xtr:InitTask> 

</xtr:RootMap>

If your static files change at regular intervals you can put instructions into the head section of the page that tells browsers not to cache them.

<head>
  <title>Generated Page</title>
  <meta http-equiv="pragma" content="no-cache"> <!-- for Netscape -->
  <meta http-equiv="expires" content="0">       <!-- for IE --> 
</head>

Services available to dxp

There are a number of services that can be used by .dxp pages. These include:

  • DbManager - pre-allocated database connections; called by various Db tags
  • AuthService - controls user access to pages; called by AuthUser tag
  • DbSessionManager - stores sessions, user selections; called by AuthUser, AnonCookie
  • DbShopper - shopping system customized to host; called by tags in dxp.biz package
  • RealmManager - manage application users; runs in an SSL realm
These are installed by the InitServlet into the 'ServletContext' of a Virtual Host. They can be accessed by DxpNodes or servlets in that host. The configuration files in each host determin what services and resources are loaded.

Configuration

The hardest part of using the dxp system is integrating it with other applications such as Apache, Resin or Tomcat, MySQL. Xerces and Java. This is addressed in the Config section.