View Javadoc
1   /*
2    * $Id$
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts.apps.mailreader.actions;
23  
24  import jakarta.servlet.ServletException;
25  import jakarta.servlet.http.HttpServletRequest;
26  import jakarta.servlet.http.HttpSession;
27  
28  import org.apache.commons.beanutils.PropertyUtils;
29  import org.apache.struts.action.ActionForm;
30  import org.apache.struts.action.ActionForward;
31  import org.apache.struts.action.ActionMapping;
32  import org.apache.struts.action.ActionMessage;
33  import org.apache.struts.action.ActionMessages;
34  import org.apache.struts.action.DynaActionForm;
35  import org.apache.struts.apps.mailreader.Constants;
36  import org.apache.struts.apps.mailreader.dao.ExpiredPasswordException;
37  import org.apache.struts.apps.mailreader.dao.Subscription;
38  import org.apache.struts.apps.mailreader.dao.User;
39  import org.apache.struts.apps.mailreader.dao.UserDatabase;
40  import org.apache.struts.extras.actions.MappingDispatchAction;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  /**
45   * <p>
46   * Base Action for MailReader application.
47   * </p><p>
48   * All the BaseAction helper methods are prefixed with "do"
49   * so that they can be easily distinguished from Struts and Servlet API methods.
50   * BaseAction subclasses may also have prive "do" helpers of their own.
51   * </p><p>
52   * Methods are kept in alphabetical order, to make them easier to find.
53   * </p>
54   *
55   * @version $Rev$ $Date$
56   */
57  public abstract class BaseAction extends MappingDispatchAction {
58      private static final long serialVersionUID = 678857024187471817L;
59  
60      // ---- Fields ----
61  
62      /**
63       * <p>
64       * Name of username field ["username"].
65       * </p>
66       */
67      public static String USERNAME = "username";
68  
69      /**
70       * <p>
71       * Name of password field ["password"].
72       * </p>
73       */
74      public static String PASSWORD = "password";
75  
76      /**
77       * <p>
78       * Name of task field ["task"].
79       * </p>
80       */
81      public final static String TASK = "task";
82  
83      // ---- Protected Variables ----
84  
85      /**
86       * The {@code Log} instance for this class.
87       */
88      private final static Logger LOG =
89          LoggerFactory.getLogger(BaseAction.class);
90  
91      // ---- Protected Methods ----
92  
93      /**
94       * <p>
95       * Store User object in client session.
96       * If user object is null, any existing user object is removed.
97       * </p>
98       *
99       * @param request The request we are processing
100      * @param user    The user object returned from the database
101      */
102     void doCacheUser(HttpServletRequest request, User user) {
103 
104         HttpSession session = request.getSession();
105         session.setAttribute(Constants.USER_KEY, user);
106         LOG.debug("LogonAction: User '{}' logged on in session {}",
107             user.getUsername(), session.getId());
108     }
109 
110     /**
111      * <p>
112      * Helper method to LOG event and cancel transaction.
113      * </p>
114      *
115      * @param session Our HttpSession
116      * @param method  Method being processed
117      * @param key     Attrkibute to remove from session, if any
118      */
119     protected void doCancel(HttpSession session, String method, String key) {
120         LOG.trace("{}{}", Constants.LOG_CANCEL, method);
121         if (key != null) {
122             session.removeAttribute(key);
123         }
124     }
125 
126     /**
127      * <p>
128      * Return the local or global forward named "failure"
129      * or null if there is no such forward.
130      * </p>
131      *
132      * @param mapping Our ActionMapping
133      * @return Return the mapping named "failure" or null if there is no such mapping.
134      */
135     protected ActionForward doFindFailure(ActionMapping mapping) {
136         LOG.trace(Constants.LOG_FAILURE);
137         return mapping.findForward(Constants.FAILURE);
138     }
139 
140     /**
141      * <p>
142      * Return the local or global forward named "logon"
143      * or null if there is no such forward.
144      * </p>
145      *
146      * @param mapping Our ActionMapping
147      * @return Return the mapping named "logon" or null if there is no such mapping.
148      */
149     protected ActionForward doFindLogon(ActionMapping mapping) {
150         LOG.trace(Constants.LOG_LOGON);
151         return mapping.findForward(Constants.LOGON);
152     }
153 
154     /**
155      * <p>
156      * Return the mapping labeled "success"
157      * or null if there is no such mapping.
158      * </p>
159      *
160      * @param mapping Our ActionMapping
161      * @return Return the mapping named "success" or null if there is no such
162      *         mapping.
163      */
164     protected ActionForward doFindSuccess(ActionMapping mapping) {
165         LOG.trace(Constants.LOG_SUCCESS);
166         return mapping.findForward(Constants.SUCCESS);
167     }
168 
169     /**
170      * <p>
171      * Helper method to fetch a String property from a DynaActionForm.
172      * </p>
173      * <p>
174      * Values are returned trimmed of leading and trailing whitespace.
175      * Zero-length strings are returned as null.
176      * </p>
177      *
178      * @param form     Our DynaActionForm
179      * @param property The name of the property
180      * @return The value or null if an error occurs
181      */
182     protected String doGet(ActionForm form, String property) {
183         String initial;
184         try {
185             initial = (String) PropertyUtils.getSimpleProperty(form, property);
186         } catch (Throwable t) {
187             initial = null;
188         }
189         String value = null;
190         if ((initial != null) && (initial.length() > 0)) {
191             value = initial.trim();
192             if (value.length() == 0) {
193                 value = null;
194             }
195         }
196         return value;
197     }
198 
199     /**
200      * <p>
201      * Obtain the cached Subscription object, if any.
202      * </p>
203      *
204      * @param session Our HttpSession
205      * @return Cached Subscription object or null
206      */
207     protected Subscription doGetSubscription(HttpSession session) {
208         return (Subscription) session.getAttribute(Constants.SUBSCRIPTION_KEY);
209     }
210 
211     /**
212      * <p>
213      * Obtain the cached Subscription object, if any.
214      * </p>
215      *
216      * @param request Our HttpServletRequest
217      * @return Cached Subscription object or null
218      */
219     protected Subscription doGetSubscription(HttpServletRequest request) {
220         HttpSession session = request.getSession();
221         return doGetSubscription(session);
222     }
223 
224     /**
225      * <p>
226      * Confirm user credentials. Post any errors and return User object
227      * (or null).
228      * </p>
229      *
230      * @param database Database in which to look up the user
231      * @param username Username specified on the logon form
232      * @param password Password specified on the logon form
233      * @param errors   ActionMessages queue to passback errors
234      * @return Validated User object or null
235      * @throws org.apache.struts.apps.mailreader.dao.ExpiredPasswordException
236      *          to be handled by Struts exception
237      *          processor via the action-mapping
238      */
239     User doGetUser(UserDatabase database, String username,
240                    String password, ActionMessages errors)
241             throws ExpiredPasswordException {
242 
243         User user = null;
244         if (database == null) {
245             errors.add(
246                     ActionMessages.GLOBAL_MESSAGE,
247                     new ActionMessage("error.database.missing"));
248         } else {
249 
250             if (username.equals("Hermes")) {
251                 throw new ExpiredPasswordException("Hermes");
252             }
253 
254             user = database.findUser(username);
255             if ((user != null) && !user.getPassword().equals(password)) {
256                 user = null;
257             }
258             if (user == null) {
259                 errors.add(
260                         ActionMessages.GLOBAL_MESSAGE,
261                         new ActionMessage("error.password.mismatch"));
262             }
263         }
264 
265         return user;
266     }
267 
268     /**
269      * <p>
270      * Confirm user credentials. Post any errors and return User object
271      * (or null).
272      * </p>
273      *
274      * @param username Username specified on the logon form
275      * @param password Password specified on the logon form
276      * @param errors   ActionMessages queue to passback errors
277      * @return Validated User object or null
278      * @throws org.apache.struts.apps.mailreader.dao.ExpiredPasswordException
279      *          to be handled by Struts exception
280      *          processor via the action-mapping
281      */
282     User doGetUser(String username,
283                    String password, ActionMessages errors)
284             throws ExpiredPasswordException {
285 
286         return doGetUser(doGetUserDatabase(), username, password, errors);
287     }
288 
289     /**
290      * <p>
291      * Return a reference to the UserDatabase
292      * or null if the database is not available.
293      * </p>
294      *
295      * @return a reference to the UserDatabase or null if the database is not
296      *         available
297      */
298     protected UserDatabase doGetUserDatabase() {
299         return (UserDatabase) servlet.getServletContext().getAttribute(
300                 Constants.DATABASE_KEY);
301     }
302 
303     /**
304      * <p>
305      * Helper method to obtain User form session (if any).
306      * </p>
307      *
308      * @param session Our HttpSession
309      * @return User object, or null if there is no user.
310      */
311     protected User doGetUser(HttpSession session) {
312         return (User) session.getAttribute(Constants.USER_KEY);
313     }
314 
315     /**
316      * <p>
317      * Helper method to obtain User form session (if any).
318      * </p>
319      *
320      * @param request Our HttpServletRequest
321      * @return User object, or null if there is no user.
322      */
323     protected User doGetUser(HttpServletRequest request) {
324         HttpSession session = request.getSession();
325         return (User) session.getAttribute(Constants.USER_KEY);
326     }
327 
328     /**
329      * <p>
330      * Save any errors and the transactioonal token, and forward to the
331      * InputForard result.
332      * </p>
333      *
334      * @param mapping Our ActionMapping
335      * @param request Our HttpServletRequest
336      * @param errors  Our ActionMessages collectoin
337      * @return The InputForward for this mappintg
338      */
339     protected ActionForward doInputForward(ActionMapping mapping,
340                                            HttpServletRequest request,
341                                            ActionMessages errors) {
342         this.saveErrors(request, errors);
343         this.saveToken(request);
344         return (mapping.getInputForward());
345     }
346 
347     /**
348      * <p>
349      * Log a "processing" message for an Action.
350      * </p>
351      *
352      * @param mapping Our ActionMapping
353      * @param method  Name of method being processed
354      */
355     protected void doLogProcess(ActionMapping mapping, String method) {
356         LOG.debug(" {}:{}{}", mapping.getPath(), Constants.LOG_PROCESSING, method);
357     }
358 
359     /**
360      * <p>
361      * Helper method to LOG event and save token.
362      * </p>
363      *
364      * @param request Our HttpServletRequest
365      */
366     protected void doSaveToken(HttpServletRequest request) {
367         LOG.trace(Constants.LOG_TOKEN);
368         saveToken(request);
369     }
370 
371     /**
372      * <p>
373      * Persist the User object, including subscriptions, to the database.
374      * </p>
375      *
376      * @param user Our User object
377      * @throws jakarta.servlet.ServletException On any error
378      */
379     protected void doSaveUser(User user) throws ServletException {
380 
381         final String LOG_DATABASE_SAVE_ERROR =
382                 " Unexpected error when saving User: ";
383 
384         try {
385             UserDatabase database = doGetUserDatabase();
386             database.save();
387         } catch (Exception e) {
388             String message = LOG_DATABASE_SAVE_ERROR + user.getUsername();
389             LOG.error(message, e);
390             throw new ServletException(message, e);
391         }
392     }
393 
394     /**
395      * <p>
396      * Helper method to inject a String property into a DynaActionForm.
397      * </p>
398      *
399      * @param form     Our DynaActionForm
400      * @param property The name of the property
401      * @param value    The value for the property
402      * @return True if the assignment succeeds
403      */
404     protected boolean doSet(ActionForm form, String property, String value) {
405         try {
406             DynaActionForm dyna = (DynaActionForm) form;
407             dyna.set(property, value);
408         } catch (Throwable t) {
409             return false;
410         }
411         return true;
412     }
413 }