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 }