Qwicap Introduction

Chris W. Johnson
Information Technology Services
The University of Texas at Austin
September 18, 2007

Qwicap is a web application development API written in Java and based on the XHTML, CSS2 and Java Servlet technologies. It dispenses with the usual hit-run-exit model of web applications, automatically provides a state-rich environment, and includes an XML "templating" engine that makes on-the-fly customization of web pages straightforward while promoting a loose coupling between page markup and code. It is intended to reduce the learning curve associated with writing web applications, and the amount of time even experienced developers spend dealing with the mechanics of such development. Furthermore, it seeks to offer an alternative to the intimate mixing of code and content promoted by Java Server Pages (JSP), the confusion and code fragmentation associated with JSP tag libraries, and, for that matter, the embedding of content into Servlet or CGI code, with the associated code-clutter and content maintenance problems.

"Qwicap" is an acronym for "Quick Web Interface for Conventional Applications." In this context, a "conventional application" is considered to be the sort of application people traditionally write when learning to program, or for use in interactive command-line environments: An application that writes output to the console, prompts its user for input whenever it wants to, blocks until the input is received, processes the input to produce some output, and continues that cycle until a desired result is achieved. While such applications often end-up with user interfaces that only their mothers could love, compared to GUI- and Web-based applications they are wonderfully quick and easy to write.

Qwicap therefore attempts, as much as possible, to make that conventional application design model work in the context of Java web applications, while doing everything it can to promote and support a high quality web interface. Where possible, it automates "best practice" (or at least "pretty good practice") behaviors.

The developer's learning curve is minimized by that approach, by Qwicap's modest, high-level API1, and by its refusal to employ crutches like metadata, config files, and custom markup. Beyond a familiarity with Qwicap's high-level API, the developer needs only a basic grasp of XHTML markup, cascading style sheets, and their application server of choice - inevitable requirements for web-oriented developers, whether they use Qwicap or not.

The conventional application model was adapted to the web and Qwicap as follows:

Brief Application Examples, Conventional vs. Qwicap

Below are conventional (interactive command-line) and Qwicap implementations of the same game in which the user attempts to guess a number between 0 and 100, inclusive. The implementations do the same things in the same order, and are approximately the same length, although certain operations require more lines of code in one version or the other. (Long lines have been wrapped and their continuations indented.)

Conventional Implementation of Number Guess

The following is a complete, self-contained implementation of the "number guess" game in the form of an interactive, command-line application. Note that the only significant overhead—code not strictly involved in implementing the central algorithm—is the user input verification loop. The Qwicap version benefits from a single method call (getInt) which provides that verification, conversion and correction-request functionality.

import java.io.InputStreamReader;
import java.io.BufferedReader;

public class CLI {
    
    public static void main(final String[] args) throws Exception {
        final BufferedReader    In = new BufferedReader(new InputStreamReader(System.in));
        final int               No = new java.util.Random().nextInt(101);
        int                     GuessCount = 0;
        int                     Guess;
        
        System.out.println("A random number between 0 and 100 (inclusive) has been selected.");
        
        do {
            while (true) {
                try {
                    System.out.print("Guess the number: ");
                    Guess = Integer.parseInt(In.readLine());
                    if (Guess >= 0 && Guess <= 100)
                        break;
                } catch (NumberFormatException e) {
                    System.out.println("That's not a number. Try again."); 
                }
            }
            
            if (Guess < No)
                System.out.println(Guess + " is too low.");
            else if (Guess > No)
                System.out.println(Guess + " is too high.");
            
            GuessCount++;
            
        } while (Guess != No);
        
        System.out.println("Correct! The number was " + No + ". You guessed it in " + 
            GuessCount + " attempts.");
    }
}

Qwicap Implementation of Number Guess

The following is an implementation of the "number guess" game in the form of a Qwicap-based web application. This is the entire implementation, but it is not complete in the sense that it depends on a web page, and a style sheet, which are not shown here.

The only significant overhead in this implementation is manipulation of the web page. There are two conceptually distinct manipulations being conducted. The first is the changing of the visibility of the web page's major divisions as the application progresses through its three phases: introduction (the initial page), interaction (playing the game), and conclusion (summarizing the outcome). This requires 2½ lines of code. The second type of manipulation inserts into the page feedback on the outcome of the user's guesses. This requires two lines of code, one in the interactive phase of play, and one to summarize the outcome. The author recognizes that this approach is not as simple as just using a few "println" statements on the command-line, but believes it's not bad. And, of course, it buys the application not only a web interface, but one that is flexible and of good quality.

import edu.utexas.its.eis.tools.qwicap.servlet.*;
import edu.utexas.its.eis.tools.qwicap.template.xml.mutable.MutableMarkup;

public class Web {
    
    public static void main(final String[] args) throws Exception {
        final Qwicap            Q = Qwicap.getCurrentInstance();
        final MutableMarkup     Doc = Q.getDocument("/number-guess.html").getMutable();
        final int               No = new java.util.Random().nextInt(101);
        int                     GuessCount = 0;
        int                     Guess = -1;
    
        do {
            try {
                Q.prompt().withoutFormData().usingPage(Doc);
                Guess = Q.getInt("guess", 0, 100, false, 0);
                
                String ResultStr = "Correct!";
                
                if (Guess < No)
                    ResultStr = "Too Low";
                else if (Guess > No)
                    ResultStr = "Too High";
                
                if (GuessCount++ == 0)
                    Doc.get("div.history").deleteClass("invisible");
                
                Doc.get("div.history tbody tr:first-child").duplicateAfter().
                    deleteClass("invisible").setContents("td.no", GuessCount, 
                    "td.guess", Guess, "td.result", ResultStr);
                    
            } catch (QwicapAbandonmentException e) {
                e.rethrowIfThisPageWasAbandoned();
            }
        } while (Guess != No);
        
    //  We're done. Setup the final page and exit.
        
        Doc.get("div.guess").delete();
        Doc.get("div.done").deleteClass("invisible").setContents("span.answer", 
            No, "span.guesses", GuessCount);
        
        Q.goodbye(Doc);
    }
}

Observe that this application effortlessly and transparently gains the benefit of sessions and server-side state. Indeed, Qwicap's approach effectively extends server-side state to encompass even the local variables in existence, and all the control-flow that is in-progress, when the prompt method is invoked. This imposes a price in server memory consumption, but given the gains in simplicity and development speed (and probably in execution speed), along with the modest cost of RAM, the author believes this to be a beneficial trade-off. The worst side-effects of this design can be mitigated by developer education: If your server might become memory-constrained, avoid holding references to large, temporary allocations in call chains leading to prompt invocations.

For additional, expanded comparisons of "number guess" implementations, see the document "Comparison of Number Guess Implementations".

1. Almost all of the public Qwicap API is supplied by two classes: Qwicap and Results.
2. Blocking a web application session to wait for input from its user necessitates executing that session in its own thread. Some server operating systems may impose serious limits on the number of concurrent threads. Such operating systems would be poor choices for running Qwicap-based web apps that could expect, individually or collectively, large numbers of simultaneous users.

In situations where such limits cannot be avoided, major sections of the Qwicap code, notably the XHTML templating mechanism, can be used independently of the full-up, thread-oriented Qwicap system.

It is the author's opinion that there are no silver bullets, and no one-size-fits-all solutions to development problems, so the fact that Qwicap isn't equally suited to all web applications is something it has in common with every other method for creating web apps. In addition, he suspects that the majority of web apps serve relatively small numbers of users concurrently, and that throwing commodity server hardware at a scaling problem can be more cost effective than throwing programmer-hours/days/weeks at it.

3. "Applicable labels" for form elements are identified through interpretation of the markup tags and attributes that were added to HTML 4 in order to assist non-visual user agents in solving the same problem. So, Qwicap encourages the creation of accessible forms.
Valid XHTML 1.0! Valid CSS!