001 package hirondelle.web4j.action;
002
003 import java.util.logging.*;
004 import java.util.regex.Pattern;
005
006 import hirondelle.web4j.request.RequestParameter;
007 import hirondelle.web4j.request.RequestParser;
008 import hirondelle.web4j.util.Util;
009 import hirondelle.web4j.model.AppException;
010
011 /**
012 <b>Template</b> for search screens.
013
014 <P>Here, a search action has the following :
015 <ul>
016 <li>it uses a form that allows the user to input search criteria
017 <li>the form must have <tt>GET</tt> as its method, not <tt>POST</tt>
018 <li>the underlying database operation is a <tt>SELECT</tt>, and does not edit the database in any way
019 </ul>
020
021 <P>Search operations never require a redirect operation (since they do not edit the database).
022
023 <P>Search operations have an interesting property : if you build a Model Object to validate and represent
024 user input into the search form, then its <tt>getXXX</tt> methods can usually be made package-private, instead
025 of <tt>public</tt>. The reason is that such Model Objects are usually not used by JSPs directly.
026 If desired, such methods can safely return <tt>String</tt> instead of
027 {@link hirondelle.web4j.security.SafeText}. (The Model Objects themselves cannot be made package-private, since
028 the {@link hirondelle.web4j.model.ModelFromRequest} class works only with <tt>public</tt> classes.)
029 */
030 public abstract class ActionTemplateSearch extends ActionImpl {
031
032 /**
033 Constructor.
034
035 @param aForward renders the result of the search
036 @param aRequestParser passed to the superclass constructor.
037 */
038 protected ActionTemplateSearch(ResponsePage aForward, RequestParser aRequestParser){
039 super(aForward, aRequestParser);
040 }
041
042 /**
043 The operations supported by this template.
044
045 <P>The supported operations are :
046 <ul>
047 <li> {@link Operation#Show}
048 <li> {@link Operation#Search}
049 </ul>
050
051 The source of the <tt>Operation</tt> is described by {@link ActionImpl#getOperation()}.
052 */
053 public static final RequestParameter SUPPORTED_OPERATION = RequestParameter.withRegexCheck(
054 "Operation", Pattern.compile("(" + Operation.Show + "|" + Operation.Search + ")")
055 );
056
057 /**
058 <b>Template</b> method.
059
060 <P>In order to clearly understand the operation of this method, here is the
061 core of its implementation, with all abstract methods in <em>italics</em> :
062 <PRE>
063 if (Operation.Show == getOperation() ){
064 //default forward
065 }
066 else if ( Operation.Search == getOperation() ){
067 <em>validateUserInput();</em>
068 if( ! hasErrors() ){
069 <em>listSearchResults();</em>
070 if ( ! hasErrors() ){
071 fLogger.fine("List executed successfully.");
072 }
073 }
074 }
075 </PRE>
076 */
077 @Override public ResponsePage execute() throws AppException {
078 if (Operation.Show == getOperation() ){
079 //default forward
080 }
081 else if ( Operation.Search == getOperation() ){
082 fLogger.fine("'Search' Operation.");
083 validateUserInput();
084 if( ! hasErrors() ){
085 fLogger.fine("Passes validation.");
086 listSearchResults();
087 if ( ! hasErrors() ){
088 fLogger.fine("List executed successfully.");
089 }
090 }
091 }
092 else {
093 throw new AssertionError("Unexpected value for Operation : " + getOperation());
094 }
095 return getResponsePage();
096 }
097
098 /**
099 Validate items input by the user into a form.
100
101 <P>The form is used to define the criteria for the search (if any).
102
103 <P>Applies only for {@link Operation#Search}. If an error occurs, then
104 <tt>addError</tt> must be called.
105 */
106 protected abstract void validateUserInput() throws AppException;
107
108 /**
109 Query the database, and place the results (usually) into request scope.
110 */
111 protected abstract void listSearchResults() throws AppException;
112
113 // PRIVATE //
114 private static final Logger fLogger = Util.getLogger(ActionTemplateSearch.class);
115 }