Cookbook

Pages

Database

Forms


Pages

  1. Getting started with your first mollypage

    First install/configure molly on your servlet capable webserver.

    Create the following page and save it as (for example) hello.mp anywhere within your website.

    <html>
    <body>
    Hello world
    </body>
    </html>
    
    When you access this page, you will see Hello World in your browser.

    Here is another example showing a molly expression in action.

    <html>
    <body>
    2 + 2 is [= 2 + 2]
    </body>
    </html>
    
    When you access this page, you will see 2 + 2 is 4 in your browser. See: All page tags
  2. Finding out the current page URL

    The page path is useful as form action parameter. For example, in a molly page:
    <form action="[=getPagePath(req)]" method="post">
    </form>
    
    This will submit the form to the same page where the form is defined. This can be hard coded of course but by using getPagePath, the html does not have to be changed if the name of the page changes on disk.

    For more information, see: getPagePath, getRealPath

  3. Accessing the request, response and out variables

    These are implicitly defined in the page and can be accesssed as-is.
    [[
    java.util.Enumeration headers = request.getHeaders();
    response.setHeader("foo", "bar");
    out.print("hello");
    ]]
    
    Shorter forms of request (req) and response (res) are also implicitly defined and it's your personal preference which one to use.
  4. Sending a client redirect

    Client redirect functionity is built into the page.
    [[
    clientRedirect(req, res, "somepage.html"); /*relative to current page*/
    ]]
    --or--
    [[
    clientRedirect(req, res, "/somepage.mp");  /*relative to context root*/
    ]]
    
    For more detail, see: Page.clientRedirect

    Also see: WebUtil for various utility methods.

  5. Sending a server side redirect

    Use the forward page tag.
    [forward somefile.mp]
    
  6. Timing page execution

    Pages have a built in timer.
    [[startTimer();]]
    <html>
    <!-- ...stuff.. -->
    The page took: [=getTime()] milliseconds;
    </html>
    
    See: PageImpl
  7. Debug output from a page

    [[dbg(true);]] <!-- set to false to turn off debugging -->
    <html>
    <!-- ...stuff.. -->
    [[
    bug(out, "some debug message");
    debug(out, "some other message");
    ]]
    </html>
    
    Note, debug (and it's shorter form bug) are aliases and both methods do the same thing. Also note, the out parameter to these methods (if you forget to pass out, then you will get a compile exception when you access the page).

    Debugging at the page level is meant to interactively debug a particular page, when the page is being written/developed.

    See: PageImpl

  8. Logging output from a page

    There are 2 output mechanisms from a page.

    1. Page level debugging: Useful when authoring a page and the output (as described above) is shown as part of HTML in the webpage itself.
    2. Page logging: which typically is persistent and goes to to a file or console

    Logging makes use of the Log class. A default logger (which writes to System.out) can be obtained via the Log.getDefault() method or equivalently via WebApp.getAppLog() method. Note: Servlet containers typically redirect System.out to either a console or a log file.

    This logger has a default logging level. This can be changed directly via Log.setDefaultLevel or via the configuration file for WebApp (if used). This logger is available from a page via the log variable. For example:

    [[
    log.info("user ABC authenticated");
    ]]
    
    This will log user ABC authenticated in your log file. Another example:
    [[
    log.debug("got a database connection", "username:", "foo", ";sid", sid);
    ]]
    
    This will send output at the debug level to your log file. Debugging statements can easily be turned off in production by settting your loglevel to INFO or higher.

    There is almost zero cost associated with simple method calls in Java (you can millions of method calls per second, even on a low end machine). All of the logging methods take an arbitrary number of parameters, so one can pass multiple strings as separate parameters to these methods and forego string concatenation.

  9. Setting a cookie

    Setting a cookie is simple, and is done the same way as in servlets.
    [[
    Cookie c = new Cookie("somename", "somevalue");
    c.setMaxAge(-1);
    res.addCookie(c);
    ]]
    
    We can easily get a session id value, from the newSessionID utility method in class SessionUtil
    [import
    import fc.web.servlet.SessionUtil;
    ]
    [[
    String sid = SessionUtil.newSessionID();
    Cookie c = new Cookie("somename", sid);
    c.setMaxAge(-1);
    res.addCookie(c);
    ]]
    
  10. Getting a cookie

    Getting a cookie from the response is simple, and is done the same way as in servlets.
    [[
    Cookie[] cookies = req.getCookies();
    if (cookies == null) 
        {
        # got no cookies <br> #
        }   
    else{
        for (int n = 0; n < cookies.length; n++) 
            {
            Cookie c = cookies[n];
            # got cookie, name:[=c.getName()], value:[=c.getValue()] <br>#
            }
        }
    ]]
    
    Note: we don't have to import the servlet classes (like Cookie). These are automatically imported in the molly page.
  11. Including compressed (gz) files in a page

    Including gzip compressed files in a page is useful for javascript library files (which are substantially smaller when gzip'ed).

    First, configure and use the GzipFileServlet

    If the GzipFileServlet is mapped to (say) /gzip, then, the following will include foo.js.gz from the server.

    <html>
    <head>
      <script src="/gzip?js=foo.js.gz"></script>
      /* foo.js will be available at this point */
    </head>
    <body>
    /*...stuff...*/
    </body>
    </html>
    
  12. Storing and using application-level data

    In the servlet world, the way to store global (application scope) data is via singleton classes. You can create your own singleton class or use the (effectively application scope) get/setAttribute mechanism provided by the servlet API (ServletContext). The servlet context also has a way to listen to when attributes are added or removed.

    Molly pages provides a singleton application level class called WebApp. This class provides various convenience functions, including get/set methods to store application level data. It's optional to use WebApp of course (you can always use ServletContext), but in practice, WebApp is a great utility class. It is recommended to read the javadocs and configure it appropriately.

    For example, in page: a.mp

    [import
    import java.util.*;
    import fc.web.servlet.WebApp;
    ]
    Setting application level data
    [[
    List list = new ArrayList();
    list.add("hello");
    list.add("world");
    WebApp.getInstance("somename").put("foo", list);
    ]]
    

    And in page: b.mp

    [import
    import java.util.*;
    import fc.web.servlet.WebApp;
    ]
    
    Got application level data: [=WebApp.getInstance("somename").get("foo")]
    
    In fact, even WebApp.get/put is only really useful for variables and properties, not for instances of classes.

    In molly pages, you do not have to use the un-necessary JSP "usebean" tags all over your code. For accessing your own classes/objects/beans, you simply define your own classes and put them in an appropriate package inside your WEB-INF/classes folder. You can then simply import and instantiate your classes directly from any page or have the classes return a singleton instance of themselves on any page.

    Note: application level storage does not imply that this data is thread-safe. Typically, the user (and not the system) is responsible for thread safety by either globally wrapping access to thread unsafe objects (inside properly locked code) or by storing/using objects that are internally thread safe.

  13. Using ThreadLocal Random, Calendar and Format objects

    The Random, Calendar, DateFormat and NumberFormat objects are not thread safe. Multiple threads could be executing a molly page at the same time. This is typically one thread for each web browser accessing the page but details vary on the servlet container.

    To keep things thread safe, we can instantiate non-thread safe objects (for example a Random object) and store them as instance variables. We would however have to synchronize access amongst all threads. For example:

    [import
    import java.util.Random;
    ]
    [!
    Random rand = new Random(); /* not thread-safe */
    !]
    
    [[
    synchronized (rand) {   /* one request at a time only */
        # Random number: [=rand.nextInt()] #
        }
    ]]
    
    To make things faster, WebApp provides access to ThreadLocal versions of these objects.
    [import
    import java.util.Random;
    import fc.util.*;
    import fc.web.servlet.WebApp;
    ]
    
    [[
    ThreadLocalRandom rand = WebApp.getInstance("somename").getThreadLocalRandom();
    
    if (rand.isNull())  {
        rand.set(new Random());
        }
    ]]
    
    Random number: [=rand.get().nextInt()]
    
    See: WebApp, ThreadLocalCalendar, ThreadLocalDateFormat, ThreadLocalNumberFormat and ThreadLocalRandom
  14. Tracking runtime errors and exceptions

    Molly pages are compiled into java source files, which are then compiled into java class files. See the process diagram and translation diagram here.

    When a runtime exception happens, the stack trace shows the line number in the java source file. You have to open the java source file to look at the lines surrounding the error (and seeing this in context will help identify where the error happened on the page). For example, in page test.mp:

    <html><head></head>
    <body>
    [[ int n = 0; ]];
    Dividing by zero gives me: [=42/n]
    </body>
    </html>
    
    This gives the following stack trace:
    Error/Exception stack trace:
    javax.servlet.ServletException: java.lang.ArithmeticException: / by zero 
    at molly.pages.test_mp.render(test_mp.java:23) 
    at fc.web.page.PageServlet.service(PageServlet.java:216) 
    ...
    
    We now open test_mp.java at line 23. molly always uses the WEB-INF/molly_tmp directory to store the generated java files. We find the following:
    22: out.print  ("Dividing by zero gives me: ");
    23: out.print  (42/n);
    24: out.print  ("\n");
    
    We can now see where this error occurred and fix it on the molly page. Note, it's important to fix errors on the molly page and not touch the java source file. This is because java source file is automatically regenerated from the molly page.
  15. Access control and requiring logins

    Suppose we require a user to login (or create a account and then login) before seeing some content. A simple pattern for this is:

    First, put all protected (requiring login) content in a sec directory. (sec stands for secure, you can use any name you like). So your website directory tree now looks like:

    document root
        |
        |-- index.html (the welcome page)
        |-- login.mp   (the login page)
        |-- sec        (secure directory)
            |
            |-- secure files
    
    Create a login.mp and put it outside the secure area (as shown above). This file looks like:
    If you don't have an account yet: request a new account.
    <h3>Please log in:</h3>
    <form name=login action=/login method=post>
    Username: <input type=text name="username"><br>
    Password: <input type=password name="password"><br>
    <input type=submit>
    </form>
    
    Requesting a new account is not shown would link to a account request page in a real application. Also, in a real login page, there would be form layout, logos, images, etc., which are all omitted here.

    Create a access control servlet filter for the sec dirctory. (this goes in your servlet web.xml file).

    <filter>
       <filter-name>JDBCAuthFilter</filter-name>
       <filter-class>fc.web.servlet.JDBCAuthFilter</filter-class>
       <init-param>
    	  <param-name>login_page</param-name>
    	  <param-value>/login.mp</param-value>
       </init-param>
    </filter>
    
    <filter-mapping>
       <filter-name>JDBCAuthFilter</filter-name>
       <!-- maps a filter to a url pattern -->
       <url-pattern>/sec/*</url-pattern>
     </filter-mapping>
    
    This filter uses the utility class JDBCAuthFilter (you can always write your own, of course). This filter redirects to the specified login page if the user is not logged in.

    Similarly, also in your web.xml file, map the /login path (used by form submit action parameter in login.mp above), as follows

    <servlet>
      <servlet-name>LoginServlet</servlet-name> 
      <servlet-class>test.MyLoginServlet</servlet-class> 
      <load-on-startup>1</load-on-startup>
      <!-- just an example, tune for your system -->
      <init-param>
        <param-name>login_query</param-name>
        <param-value>select * from users where username=? and passwd=?</param-value> 
      </init-param>
      <init-param>
        <param-name>password_hash</param-name>
        <param-value>MD5</param-value> 
      </init-param>
      <init-param>
        <param-name>login_page</param-name>
        <param-value>/login.mp</param-value> 
      </init-param>
      <init-param>
        <param-name>welcome_page</param-name>
        <param-value>/sec/index.mp</param-value> 
      </init-param>
    </servlet>
    
    <servlet-mapping>
      <servlet-name>LoginServlet</servlet-name>
      <url-pattern>/login</url-pattern>
    </servlet-mapping>
    
    Finally, MyLoginServlet (which would be placed in WEB-INF/classes/test/ (since it's in package test), looks like:
    package test;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.sql.*; 
    import fc.web.servlet.*;
    
    /**
    MyLoginServlet saves user and possibly other information to the session upon
    successful login. It relies on fc.web.servlet.LoginServlet for most
    of it's work.
    
    This entire class is optional, we could have just used fc.web.servlet.LoginServlet 
    directly, if we wanted. This example/class presumes a Users table, a is_admin column,
    etc.
    */
    public final class MyLoginServlet extends fc.web.servlet.LoginServlet
    {
    public void onLogin(Connection con, String sid, String username, 
        HttpServletRequest req,  HttpServletResponse res) 
    throws SQLException, IOException
        {
        boolean is_admin = false;
        users u = usersMgr.getByKey(con, username);
        if (u != null) {
            is_admin = u.get_is_admin();
            }
        JDBCSession.getInstance().put(
        	con, sid, "is_admin", Boolean.toString(is_admin));
        }
    }
    
  16. See: JDBCAuthFilter, LoginServlet

Database

  1. Getting a database connection on a page

    The general pattern for any page that requires a database connection is as follows:
    [import
    import java.sql.*;
    import fc.jdbc.*;
    import fc.web.servlet.WebApp;
    ]
    
    [[
    Connection con = null;
    try 
    {
    con = WebApp.getInstance("somename").getConnection();
    ]]
    
    <html>
    <head>
    </head>
    <body>
    Got connection: [=con]	
    
    </body>
    </html>
    
    [[
    } //end try block
    finally {
        con.close();
        }
    ]]
    
    
    Note, this is a simple and reliable pattern.

    The connection is:

    1. always obtained in a try block at the very beginning of the page
    2. always closed in a finally block at the very end of the page (after the closing </html> tag)

    One typically has other try..catch..finally blocks in the page. But this outermost try..finally block is uncluttered with any other concern, it simple always gets and closes a connection.

    See also: WebApp. Above, we obtained a connection via WebApp but we don't have to, we can always get a connection directly, via JDBC. For example, here is a bare-bones way of getting a connection:

    [import
    import java.sql.*;
    ]
    [!
    String url = "jdbc:postgresql://127.0.0.1:5432/test";
    String user = "postgres";
    String password = "xxx";
    !]
    [[
    Connection con = null;
    try 
    {
    con = DriverManager.getConnection(url,user,password);
    ]]
    
    <html>
    <head>
    </head>
    <body>
    Got connection: [=con]	
    </body>
    </html>
    
    [[
    } //end try block
    finally {
        con.close();
        }
    ]]
    
    This bare-bones way is for illustration only and a connection pool (or the molly WebApp way shown above, which uses a connection pool internally) is recommended for real applications.
  2. Making a database query using a JDBC statement

    This larger example shows using the connection within the page.
    [import
    import java.sql.*;
    import fc.jdbc.*;
    import fc.web.servlet.WebApp;
    ]
    
    [[
    dbg(true);
    startTimer();
    ]]
    
    [[
    Connection con = null;
    try 
    { 
    con = WebApp.getInstance("somename").getConnection();
    ]]
    
    <html>
    <head>
    </head>
    <body>
    Got connection: [=con]  
    [[
    Statement stmt = con.createStatement(   
                        ResultSet.TYPE_SCROLL_INSENSITIVE,
                        ResultSet.CONCUR_READ_ONLY);
    
    ResultSet rs = stmt.executeQuery("select 1 + 1");
    if (rs != null) 
        {   
        /* fc.jdbc.QueryUtil is a simple utility class */
        out.print("Got: " + QueryUtil.getRowCount(rs) + " rows");
        QueryUtil.printResultSetHTMLTable(rs, out, QueryUtil.ResultSetPrintDirection.HORIZONTAL);
        }
    ]]
    </body>
    </html>
    [[
    } //end outermost try
    finally {
    	con.close();
        }
    ]]
    
    [[
    bug(out, "Page time: ", getTime(), " milliseconds");
    ]]
    
    Also see: QueryUtil
  3. Using O/R generated classes

    Suppose we have a table Users in our database, defined as:
      Column  |          Type          | 
    ----------+------------------------+
     name     | character varying(255) | 
     email    | character varying(255) | 
     password | character varying(255) |
    
    When we run our DBO generator, we end up with 2 classes. We tell our generator to put these classes in a package called test(for example).
    Users.java
    UsersMgr.java
    
    We then compile these classes and put them, where our servlet container can find them. A good place to put them is: /WEB-INF/classes/test (since they were in package test)

    To use these classes in a mollypage, we now say:

    [import
    import java.sql.*;
    import java.util.*;
    import fc.jdbc.*;
    import fc.web.servlet.WebApp;
    import test.*
    ]
    
    [[
    Connection con = null;
    try 
    { 
    con = WebApp.getInstance("somename").getConnection();
    ]]
    
    <html>
    <head>
    </head>
    <body>
    All the users in the system are:
    <table>
    <tr>
    	<td>Name</td>
    	<td>Email</td>
    </tr>
    [[
    List list = UsersMgr.getAll(con);
    for (int n = 0; n < list.size(); n++)
        {
        Users u = (Users) list.get(n);
        #
        <tr>
            <td>[=u.get_name()]</td>
            <td>[=u.get_email()]</td>
        </tr>
        #
        }
    ]]
    </table>
    </body>
    </html>
    [[
    } //end outermost try
    finally {
    	con.close();
        }
    ]]
    
    
    Note: the DBO method names get_name & get_email in the example above, can differ based on your preference in the DBO generator configuration file. For instance, getName & getEmail could also have been generated.
  4. Committing/saving data to a database

    JDBC transactions are defined per each JDBC connection. By default, new connections are in auto-commit mode, which means each statement is executed and committed immediately.

    However, it is a better idea to use the same connection to do a unit of work and then commit or rollback the result depending on success/failture for the unit as a whole.

    For this reason, methods in various DBO-generated manager classes take a connection as the first parameter. For example, we may save 2 objects, say Users and UsersDetail together. If both saves succeed, we call con.commit() and if either fails, we call con.rollback(). This ensures that there is no halfway write. Either all data (or no data) is saved to the database.

    [import
    import java.sql.*;
    import java.util.*;
    import fc.jdbc.*;
    import fc.web.servlet.WebApp;
    import test.*
    ]
    
    [[
    Connection con = null;
    try 
    { 
    con = WebApp.getInstance("somename").getConnection();
    con.setAutoCommit(false);
    ]]
    
    <html>
    <head>
    </head>
    <body>
    [[
    Users user = new Users();
    user.set_name("hiro protoganist");
    UsersDetail userdetail = new UsersDetail();
    userdetail.set_drug("snow crash");
    
    UsersMgr.save(con, user);
    UsersMgr.save(con, userdetail);
    
    con.commit();
    ]]
    </table>
    </body>
    </html>
    [[
    } //end outermost try
    catch (SQLException e) {
        con.rollback();
        }
    finally {
        con.close();
        }
    ]]
    
    Note, we said above that new connections are in auto-commit mode. This is true only for unpooled connections. If we use WebApp to get a connection (as shown above), we will get a connection from a internal connection pool. Once we set the con.setAutoCommit(true|false) of that connection, the connection will remember that setting (even when it's closed and reopened) until another call to con.setAutoCommit(true|false)(if needed to change the mode again) in the future.

    Since we don't always know the state/history of the connection, it is always a good idea to say: con.setAutoCommit(false) at the beginning of our page.

  5. Storing session data in a database

    Memory based sessions are fragile and non-scalable. Session migration using serialization is unreliable, buggy, hard to configure and use.

    Enter JDBCSession, which allows you to save session information to a database. (this is quite fast, on the order of milliseconds, typically). This also allows one to trivially scale horizontally to as many front-end servlet/web servers as one needs.

    The following example shows this in action:

    [import
    import java.util.*;
    import java.sql.*;
    import fc.web.servlet.*;
    ]
    
    [[
    Connection con = null;
    try 
    { 
    con = WebApp.getInstance("somename").getConnection();
    String sid = SessionUtil.newSessionID();
    JDBCSession session = JDBCSession.getInstance();
    session.create(con, sid);
    ]]
    
    Session exists: [=session.exists(con, sid)]<br>
    Session info: 	[=session.sessionInfo(con, sid)]
    
    [[
    session.add(con, sid, "apple", "orange");		
    Map map = new HashMap();
    map.put("num", 123);
    map.put("phrase", "hello");
    session.addAll(con, sid, map);		
    ]]
    
    All values in session:<br>[=session.getAll(con, sid)]
    
    [[
    } //end outermost try
    finally {
    	con.close();
        }
    ]]
    
  6. Scaling your website

    As per the above item, if you store session data in the database, then you can horizontally scale your website easily.


    (click for full size)

  7. Caching database results

    Sometimes, when we know a database table doesn't change too often, and we need to use this table in a page, it is good to be able to cache the content of the table (so we dont have to access the database every time). This is typically the case for static lookup tables, for example, the 50 USA states.

    Your default mode should be to look up everything, all the time, when developing a website. Premature optimization like lookuptable caching is a waste of keystrokes. When you want to move to production, then it may be useful.

    
    [import
    /*...other imports here..*/
    import fc.web.servlet.WebApp;
    import fc.util.cache.*;
    /*..*/
    ]
    
    [[
    Cache cache = WebApp.getInstance("somename").getDBCache();
    List states = (List) cache.get("lookup_states");
    if (states == null) {
        states = statesMgr.getAll(con);
        cache.put("lookup_states", states);
        }
    
    ]]
    
    <form>
    <select name="states">
    [[
    for (int n = 0; n < states.size(); n++) {
        state s = (state) states.get(n);
        # <option value="[=s.get_code()">[=s.get_name()]</option> #
        }
    ]]
    </select>
    
    
    See: Cache

Forms

  1. Maintaining form state

    When a user fills and submits a HTML form, it is a good idea to always validate it on the server (regardless of client-side validation). It is common for the form to have errors and to be redisplayed to the user.

    It is a huge pain (and a good user-shedding device) to lose the already filled out information and show a blank form to the user. Molly allows one to easily maintain form state and avoid this problem. Here is a simple example:

    
    [import
    import fc.web.simpleforms.*;
    ]
    <form method=post>
    fname:	
    <input type=text name=fname value='[= State.text(req, "fname")]'><br>
    
    sex:
    <input type=radio name=sex value=male   [= State.radiogroup(req, "sex", "male")]>male
    <input type=radio name=sex value=female [= State.radiogroup(req, "sex", "female")]>female<br>
    
    comment:
    <textarea name=comment rows=3 >[=State.textarea(req, "comment")]</textarea><br>
    
    mood: 	
    <select name=mood comment><br>
    <option value=dunno [=State.select(req, "mood", "dunno")]>select-a-mood</option>
    <option value=happy [=State.select(req, "mood", "happy")]>happy</option>
    <option value=sad   [=State.select(req, "mood", "sad")]>sad</option>
    </select><br>
    
    agree:
    <input type=checkbox name=agree value=yes [= State.checkbox(req, "agree")]><br>
    
    <input type=submit>
    </form>
    
    Putting state preserving code in form tags (as shown above) is a common pattern. Molly pages gives you the State class (in the simpleforms package), which simplifies the backend logic for you.

    Also: see these general (and essential) utility methods:
    quoteToEntity, entityToQuote

  2. Abstracting form elements via Java objects

    Molly also gives you an additional way of working with HTML forms, via the forms package. This can be used as an alternative to simpleforms. Instead of defining form input elements in HTML (and putting in manual state preserving statements, as shown above), one creates Java objects that represent a HTML form element. These form elements are smart and automatically preserve their own state.

    For example:

    [import
    import fc.web.forms.*;
    import java.util.*;
    import java.sql.*;
    import fc.web.servlet.*;
    ]
    
    [!
    Form makeForm()
    {
        //retrieve if we already made it before
        Form form = WebApp.getInstance("somename").getForm("test"); 
        if (form != null) {
            return form;
            }
            
        form = new Form("test");
    
        Text fname = new Text("fname");
        fname.setSize(20).setMaxSize(64);
        form.add(fname);
    
        RadioGroup sex = new RadioGroup("sex");
        sex.add(new ChoiceGroup.Choice("male", false));
        sex.add(new ChoiceGroup.Choice("female", false));
        form.add(sex);
        
        TextArea comment = new TextArea("comment");
        form.add(comment);
        
        ArrayList moods = new ArrayList();
        moods.add(new Select.Option("dunno", true));
        moods.add(new Select.Option("happy"));
        moods.add(new Select.Option("sad"));
        Select mood = new Select("mood", moods);
        form.add(mood);
        
        Checkbox agree = new Checkbox("agree");
        form.add(agree);
    
        WebApp.getInstance("somename").putForm(form);
        return form;
        }
    !]	
    
    [[
    Form form = makeForm();
    FormData fd = form.handleSubmit(req); //maintain state
    ]]
    
    <form method=post>
    fname:  
    [[ form.get("fname").render(fd, out); ]] <br>    
    sex:
    [[ form.get("sex").render(fd, out); ]] <br>
    comment:
    [[ form.get("comment").render(fd, out); ]] <br>
    mood:   
    [[ form.get("mood").render(fd, out); ]] <br>
    agree:
    [[ form.get("agree").render(fd, out); ]] <br>
    <input type=submit>
    </form>
    
    This form will automatically maintain it's state.

    When a form is submitted, it is processed for errors. There can be field level errors and form level errors. This example shows the automatic rendering of both.

    [[
    Form form = makeForm();
    FormData fd = form.handleSubmit(req); //maintain state
    form.renderError(fd, out);  
    ]]
    
    <form method=post>
    fname:  
    [[ form.get("fname").renderError(fd, out, "<br>").render(fd, out); ]] <br>    
    sex:
    [[ form.get("sex").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    comment:
    [[ form.get("comment").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    mood:   
    [[ form.get("mood").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    agree:
    [[ form.get("agree").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    <input type=submit>
    </form>
    


    Typing all of the above together, here is a full example of the validation process.

    [import
    import fc.web.forms.*;
    import java.util.*;
    import java.sql.*;
    import fc.web.servlet.*;
    ]
    
    [!
    Form makeForm()
    {
        //retrieve if we already made it before
        Form form = WebApp.getInstance("somename").getForm("test"); 
        if (form != null)
            return form;
        
        form = new Form("test");
    
        Text fname = new Text("fname");
        fname.setSize(20).setMaxSize(64);
        form.add(fname);
    
        RadioGroup sex = new RadioGroup("sex");
        sex.add(new ChoiceGroup.Choice("male", false));
        sex.add(new ChoiceGroup.Choice("female", false));
        form.add(sex);
        
        TextArea comment = new TextArea("comment", 3, 20); //3 rows, 20 cols
        form.add(comment);
        
        ArrayList moods = new ArrayList();
        moods.add(new Select.Option("dunno", true));
        moods.add(new Select.Option("happy"));
        moods.add(new Select.Option("sad"));
        Select mood = new Select("mood", moods);
        form.add(mood);
        
        Checkbox agree = new Checkbox("agree");
        form.add(agree);
    
        //This will automatically add validation based on database schema
        //(very cool but advanced feature, study the javadocs)
        //UsersMgr.addValidators(f);
    
        WebApp.getInstance("somename").putForm(form);
        return form;
        }
    
    FormData processForm(HttpServletRequest req, Form form) 
    throws ServletException, IOException
        {
        FormData fd = form.handleSubmit(req);
        
        Text fname       = (Text) form.get("fname");
        String fname_val = fname.getValue(fd);
        
        if (fname_val.length() < 3) {
            fname.addError(fd, "Please choose a longer username");
            }
    
        /*  
        Assuming a UsersMgr DBO object to talk to the database. Can use a manual
        JDBC statement here as well. 
        */
        if (UsersMgr.exists(con, fname_val)) {
            fname.addError(fd, "This user name already exists. Please choose another name");
            }
        
        /* validation for other fields here as needed */
        
        return fd;
        }
    !]
    
    [[
    Form form = makeForm();
    FormData fd = null;
    
    if (req.getParameter("submit") != null)
        {
        fd = processForm(req, form);
        }
    
    //form level errors (if any)
    form.renderError(fd, out);
    ]]
    
    <form method=post>
    fname:  
    [[ form.get("fname").renderError(fd, out, "<br>").render(fd, out); ]] <br>    
    sex:
    [[ form.get("sex").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    comment:
    [[ form.get("comment").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    mood:   
    [[ form.get("mood").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    agree:
    [[ form.get("agree").renderError(fd, out, "<br>").render(fd, out); ]] <br>
    <input type=submit name=submit value=submit>
    </form>
    
    You can use either the abstracted form or the simple form approach based on your personal preference/aversion to complexity.