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    }