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 }