Example App

Initially, the available login IDs are admin, secofr, and john,
and the passwords are the same as the login ID.
Log In
:
:
    Home
A page can be made secure, ie. accessible only by HTTPS, by annotating the page's class with @Secure.
You will also have to enable HTTPS on your web server, which may require some configuration.
References: Securing your application with HTTPS, How to enable SSL on JBoss.
Login.tml


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- We need a doctype to allow us to use special characters like &nbsp; 
     We use a "strict" DTD to make IE follow the alignment rules. -->
     
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">

<head>
    <title>Log In</title>
    <link rel="stylesheet" type="text/css" href="${context:css/theapp/login.css}"/>
    <t:remove>
        <!-- This link allows previewability -->
        <link rel="stylesheet" type="text/css" href="../css/theapp/login.css"/>
    </t:remove>
</head>

<body>
<div id="page">

    <div id="titlesbar">
        <h1>Example App</h1>
    </div>

    <div id="instructions">
        Initially, the available login IDs are admin, secofr, and john, <br/>
        and the passwords are the same as the login ID.
    </div>

    <form t:type="form" t:id="login">
        <div class="title">
            Log In
        </div>
        <table>
            <tr>
                <th><t:label for="loginId"/>:</th>
                <td><input t:type="TextField" t:id="loginId" size="15" maxLength="12" t:validate="required, maxLength=15"/></td>
            </tr>
            <tr>
                <th><t:label for="password"/>:</th>
                <td><input t:type="PasswordField" t:id="password" size="15" maxLength="12" t:validate="required, maxLength=15"/></td>
            </tr>
            <tr>
                <th>&nbsp;</th>
                <td class="buttons">
                    <input type="submit" value="Login"/>&nbsp;
                    <a t:type="eventlink" t:event="GoHome" href="#">Home</a>
                </td>
            </tr>
            <tr>
                <td colspan="2">            
                    <div id="errors">
                        <t:errors/>
                    </div>
                </td>
            </tr>
        </table>
    </form>

    <div id="secure">
        A page can be made secure, ie. accessible only by HTTPS, by annotating the page's class with <code>@Secure</code>.<br/>
        You will also have to enable HTTPS on your web server, which may require some configuration.<br/>
        References:
        <a href="http://tapestry.apache.org/https.html">Securing your application with HTTPS</a>, 
        <a href="http://roneiv.wordpress.com/2008/01/03/jboss-tutorial-how-to-enable-ssl-https-on-jboss-as-well-as-other-nice-to-know-configurations/">How to enable SSL on JBoss</a>.
    </div>
</div>

<div id="source">
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/pages/theapp/Login.tml"/>
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/pages/theapp/Login.java"/>
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/commons/IIntermediatePage.java"/>
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/css/theapp/login.css"/>
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/state/theapp/Visit.java"/>
    <t:sourcecodedisplay src="/web/src/main/java/jumpstart/web/base/theapp/SimpleBasePage.java"/>
</div>

</body>
</html>

Login.java


package jumpstart.web.pages.theapp;

import javax.ejb.EJB;

import jumpstart.business.commons.exception.BusinessException;
import jumpstart.business.domain.security.User;
import jumpstart.business.domain.security.iface.ISecurityFinderServiceLocal;
import jumpstart.util.ExceptionUtil;
import jumpstart.web.base.theapp.SimpleBasePage;
import jumpstart.web.commons.IIntermediatePage;
import jumpstart.web.pages.Index;
import jumpstart.web.pages.theapp.general.Welcome;
import jumpstart.web.state.theapp.Visit;

import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.annotations.Component;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.corelib.components.TextField;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.slf4j.Logger;

// To make this page accessible only by HTTPS, annotate it with @Secure and ensure your web server can deliver HTTPS.
// See http://tapestry.apache.org/secure.html .
// @Secure
public class Login extends SimpleBasePage implements IIntermediatePage {

    // The activation context

    @Property
    private String loginId;

    // Screen fields

    @Property
    private String password;

    // Generally useful bits and pieces

    @Persist
    private Link nextPageLink;

    @Component(id = "login")
    private Form form;

    @Component(id = "loginId")
    private TextField loginIdField;

    @Inject
    private Logger logger;
    
    @Inject
    private ComponentResources componentResources;
    
    @EJB
    private ISecurityFinderServiceLocal securityFinderService;

    // The code
    
    @Override
    public void setNextPageLink(Link nextPageLink) {
        this.nextPageLink = nextPageLink;
    }

    String onPassivate() {
        return loginId;
    }

    void onActivate(String loginId) {
        this.loginId = loginId;
    }

    void onValidateFromLogin() {

        if (form.getHasErrors()) {
            // We get here only if a server-side validator detected an error.
            return;
        }

        try {
            // Authenticate the user

            User user = securityFinderService.authenticateUser(loginId, password);

            // Store the user in the Visit

            setVisit(new Visit(user));
            logger.info(user.getLoginId() + " has logged in.");
        }
        catch (BusinessException e) {
            form.recordError(loginIdField, e.getLocalizedMessage());
        }
        catch (Exception e) {
            logger.error("Could not log in.  Stack trace follows...");
            logger.error(ExceptionUtil.printStackTrace(e));
            form.recordError(getMessages().get("login_problem"));
        }
    }

    Object onSuccess() {
        
        if (nextPageLink == null) {
            return Welcome.class;
        }
        else {
            componentResources.discardPersistentFieldChanges();
            return nextPageLink;
        }

    }
    
    Object onGoHome() {
        componentResources.discardPersistentFieldChanges();
        return Index.class;
    }

}

IIntermediatePage.java


package jumpstart.web.commons;

import org.apache.tapestry5.Link;

public interface IIntermediatePage {
    
    void setNextPageLink(Link nextPageLink);

}

login.css


body            { font-family: Arial, Helvetica, sans-serif; font-size: 12px; font-weight: normal; margin: 0; width: 100%; }

a               { text-decoration: none; color: #3D69B6; }
a:hover         { text-decoration: underline; }

#page           { margin: 0 auto; background-color: #f8f8f8; 
                    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f4f4f4', endColorstr='#ffffff'); /* for IE */
                    background: -webkit-gradient(linear, left top, left bottom, from(#eaeaea), to(#ffffff)); /* for webkit browsers */
                    background: -moz-linear-gradient(top, #f2f2f2, #ffffff); /* for firefox 3.6+ */; }

#titlesbar      { padding: 16px 0; }
#titlesbar h1   { font-size: 22px; margin: 0px auto 6px; text-align: center; color: #444; } 

#instructions   { padding: 80px 0 24px; text-align: center; color: #666; }

#login              { display: table; margin: auto; text-align: center; padding: 8px 10px; background-color: #fff; width: 280px; font-size: 14px; 
                        border: 1px solid #ddd; border-radius: 6px; -webkit-border-radius: 6px; -moz-border-radius: 6px;
                        box-shadow: 0 1px 4px rgba(0, 0, 0, .065); -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, .065); -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, .065); }
#login .title       { margin: auto; padding: 10px; color: #333; font-size: 16px; font-weight: bold; }
#login table        { border-collapse: collapse; border-spacing: 0; width: 100%; }
#login th           { color: #666; font-size: 14px; font-weight:normal; text-align: right; }
#login td           { width: 60%; padding: 4px; color: #666; font-size: 14px; font-weight: bold; text-align: left; }
#login td.buttons   { padding: 8px 2px; margin: auto; text-align: left; font-weight: normal; }
#login td.buttons a { padding-top: 20px; }

#errors     { }
#errors div.t-error             { margin: 0; border: none; }
#errors div.t-error div.t-banner    { display: none; }
#errors div.t-error ul          { color: inherit; background-color: inherit; list-style: none; margin: 0; }
#errors div.t-error li          { color: red; background-color: inherit; text-align: center; font-weight: normal; }

#secure     { padding: 80px 0; text-align: center; color: #666; }

#source     { padding: 8px; background-color: #fff; }

Visit.java


package jumpstart.web.state.theapp;

import java.io.Serializable;

import jumpstart.business.domain.security.User;
import jumpstart.business.domain.security.User.PageStyle;

@SuppressWarnings("serial")
public class Visit implements Serializable {

    private Long myUserId = null;
    private String myLoginId = null;
    private PageStyle pageStyle = null;
    private String dateInputPattern = null;
    private String dateViewPattern = null;
    private String dateListPattern = null;
    
    public Visit(User user) {
        myUserId = user.getId();
        cacheUsefulStuff(user);
    }

    public void noteChanges(User user) {
        if (user == null) {
            throw new IllegalArgumentException();
        }
        else if (user.getId().equals(myUserId)) {
            cacheUsefulStuff(user);
        }
    }

    private void cacheUsefulStuff(User user) {
        myLoginId = user.getLoginId();
        pageStyle = user.getPageStyle();
        dateInputPattern = user.getDateInputPattern();
        dateViewPattern = user.getDateViewPattern();
        dateListPattern = user.getDateListPattern();
    }

    public Long getMyUserId() {
        return myUserId;
    }

    public String getMyLoginId() {
        return myLoginId;
    }

    public PageStyle getPageStyle() {
        return pageStyle;
    }

    public String getDateInputPattern() {
        return dateInputPattern;
    }

    public String getDateViewPattern() {
        return dateViewPattern;
    }

    public String getDateListPattern() {
        return dateListPattern;
    }

}

SimpleBasePage.java


package jumpstart.web.base.theapp;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

import jumpstart.business.commons.exception.BusinessException;
import jumpstart.business.commons.exception.CannotDeleteIsReferencedException;
import jumpstart.business.commons.exception.DuplicateAlternateKeyException;
import jumpstart.business.commons.exception.DuplicatePrimaryKeyException;
import jumpstart.business.commons.exception.OptimisticLockException;
import jumpstart.business.commons.interpreter.BusinessServiceExceptionInterpreter;
import jumpstart.business.domain.security.User;
import jumpstart.web.state.theapp.Visit;

import org.apache.tapestry5.annotations.SessionState;
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.annotations.Inject;

public class SimpleBasePage {

    // @SessionState is explained in http://tapestry.apache.org/session-storage.html
    @SessionState
    private Visit visit;
    private boolean visitExists;

    private BusinessServiceExceptionInterpreter businessServiceExceptionInterpreter = new BusinessServiceExceptionInterpreter();

    @Inject
    private Messages messages;

    protected Messages getMessages() {
        return messages;
    }

    public Visit getVisit() {
        return visit;
    }

    protected void setVisit(Visit visit) {
        this.visit = visit;
    }
    
    public boolean isVisitExists() {
        return visitExists;
    }

    public String getDateInputPattern() {
        return visitExists ? visit.getDateInputPattern() : User.defaultDateInputPattern;
    }

    public String getDateViewPattern() {
        return visitExists ? visit.getDateViewPattern() : User.defaultDateViewPattern;
    }

    public String getDateListPattern() {
        return visitExists ? visit.getDateListPattern() : User.defaultDateListPattern;
    }
    
    public DateFormat getDateInputFormat() {
        // If you want to make this static or move it into Visit, first read
        // http://thread.gmane.org/gmane.comp.java.tapestry.user/20925
        return new SimpleDateFormat(visit.getDateInputPattern());
    }

    public DateFormat getDateViewFormat() {
        // If you want to make this static or move it into Visit, first read
        // http://thread.gmane.org/gmane.comp.java.tapestry.user/20925
        return new SimpleDateFormat(visit.getDateViewPattern());
    }

    public DateFormat getDateListFormat() {
        // If you want to make this static or move it into Visit, first read
        // http://thread.gmane.org/gmane.comp.java.tapestry.user/20925
        return new SimpleDateFormat(visit.getDateListPattern());
    }

    protected String interpretBusinessServicesExceptionForCreate(Exception e) {
        String message = "";
        BusinessException x = businessServiceExceptionInterpreter.interpret(e);

        if (x instanceof DuplicatePrimaryKeyException) {
            message = getMessages().get("create_failed_duplicate_primary_key");
        }
        else if (x instanceof DuplicateAlternateKeyException) {
            DuplicateAlternateKeyException d = (DuplicateAlternateKeyException) x;
            message = getMessages().format("create_failed_duplicate_alternate_key", d.getTechnicalMessageText());
        }
        else {
            message = x.getMessage();
        }
        return message;
    }
    
    protected BusinessException interpretBusinessServicesException(Exception e) {
        return businessServiceExceptionInterpreter.interpret(e);
    }

    protected String interpretBusinessServicesExceptionForAdd(Exception e) {
        String message = "";
        BusinessException x = businessServiceExceptionInterpreter.interpret(e);

        if (x instanceof OptimisticLockException) {
            message = getMessages().get("add_failed_optimistic_lock");
        }
        else if (x instanceof DuplicatePrimaryKeyException) {
            message = getMessages().get("add_failed_duplicate_primary_key");
        }
        else if (x instanceof DuplicateAlternateKeyException) {
            DuplicateAlternateKeyException d = (DuplicateAlternateKeyException) x;
            message = getMessages().format("add_failed_duplicate_alternate_key", d.getTechnicalMessageText());
        }
        else {
            message = x.getMessage();
        }
        return message;
    }

    protected String interpretBusinessServicesExceptionForChange(Exception e) {
        String message = "";
        BusinessException x = businessServiceExceptionInterpreter.interpret(e);

        if (x instanceof OptimisticLockException) {
            message = getMessages().get("change_failed_optimistic_lock");
        }
        else if (x instanceof DuplicateAlternateKeyException) {
            DuplicateAlternateKeyException d = (DuplicateAlternateKeyException) x;
            message = getMessages().format("change_failed_duplicate_alternate_key", d.getTechnicalMessageText());
        }
        else {
            message = x.getMessage();
        }
        return message;
    }

    protected String interpretBusinessServicesExceptionForRemove(Exception e) {
        String message = "";
        BusinessException x = businessServiceExceptionInterpreter.interpret(e);

        if (x instanceof OptimisticLockException) {
            message = getMessages().get("remove_failed_optimistic_lock");
        }
        else if (x instanceof CannotDeleteIsReferencedException) {
            CannotDeleteIsReferencedException c = (CannotDeleteIsReferencedException) x;
            message = getMessages().format("remove_failed_is_referenced",
                    new Object[] { c.getReferencedByEntityName() });
        }
        else {
            message = x.getMessage();
        }
        return message;
    }

    protected String interpretBusinessServicesExceptionForDelete(Exception e) {
        String message = "";
        BusinessException x = businessServiceExceptionInterpreter.interpret(e);

        if (x instanceof OptimisticLockException) {
            message = getMessages().get("delete_failed_optimistic_lock");
        }
        else if (x instanceof CannotDeleteIsReferencedException) {
            CannotDeleteIsReferencedException c = (CannotDeleteIsReferencedException) x;
            message = getMessages().format("delete_failed_is_referenced",
                    new Object[] { c.getReferencedByEntityName() });
        }
        else {
            message = x.getMessage();
        }
        return message;
    }

}