View Javadoc

1   /*
2    * The SmartWeb Framework
3    * Copyright (C) 2004-2006
4    *
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18   *
19   * For further informations on the SmartWeb Framework please visit
20   *
21   *                        http://smartweb.sourceforge.net
22   */
23  package net.smartlab.web;
24  
25  import java.io.InputStream;
26  import java.util.ArrayList;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.MissingResourceException;
31  import java.util.Properties;
32  
33  import javax.servlet.ServletContext;
34  import javax.servlet.http.HttpServletRequest;
35  import javax.servlet.http.HttpServletResponse;
36  
37  import net.smartlab.web.bean.Valorizer;
38  
39  import org.apache.commons.beanutils.locale.LocaleBeanUtilsBean;
40  import org.apache.commons.beanutils.locale.LocaleConvertUtilsBean;
41  import org.apache.commons.collections.FastHashMap;
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  import org.apache.struts.Globals;
45  import org.apache.struts.action.ActionErrors;
46  import org.apache.struts.action.ActionForm;
47  import org.apache.struts.action.ActionForward;
48  import org.apache.struts.action.ActionMapping;
49  import org.apache.struts.action.ActionMessage;
50  import org.apache.struts.action.ActionMessages;
51  import org.apache.struts.action.DynaActionForm;
52  
53  /**
54   * This class represents the <i>controller </i> part of the <b>MVC </b> pattern.
55   * Extend this class to provide a specific set of operations to be performed in
56   * response of a user selection.
57   * 
58   * @author rlogiacco
59   * @see org.apache.struts.action.Action
60   * @uml.dependency supplier="net.smartlab.web.ActionException"
61   * @uml.dependency supplier="net.smartlab.web.bean.Valorizer"
62   */
63  public abstract class Action extends org.apache.struts.action.Action {
64  
65  	/**
66  	 * Identifies the default resource the request will be forwarded.
67  	 */
68  	public final static ActionForward DEFAULT_FORWARD = new ActionForward("default");
69  
70  	/**
71  	 * Provides logging capabilities to the action.
72  	 */
73  	protected final Log logger = LogFactory.getLog(this.getClass());
74  
75  
76  	/**
77  	 * This method performs some common operations then redirects control to the
78  	 * abstract <code>execute</code>.
79  	 * 
80  	 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping,
81  	 *      org.apache.struts.action.ActionForm,
82  	 *      javax.servlet.http.HttpServletRequest,
83  	 *      javax.servlet.http.HttpServletResponse)
84  	 */
85  	public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
86  			HttpServletResponse response) throws Exception {
87  		if (logger.isDebugEnabled()) {
88  			logger.debug("execute(" + mapping.getPath() + ") - start");
89  		}
90  		ActionForward forward = null;
91  		if (super.isCancelled(request)) {
92  			if (logger.isDebugEnabled()) {
93  				logger.debug("execute(" + mapping.getPath() + ") - cancel");
94  			}
95  			forward = this.cancel(form, request, response, mapping);
96  			if (forward == DEFAULT_FORWARD) {
97  				return mapping.getInputForward();
98  			} else {
99  				return forward;
100 			}
101 		}
102 		forward = this.execute(form, request, response, mapping);
103 		try {
104 			if (request.getAttribute(Globals.ERROR_KEY) != null && forward == DEFAULT_FORWARD) {
105 				if (logger.isDebugEnabled()) {
106 					logger.debug("execute(" + mapping.getPath() + ") - errors");
107 					if (logger.isTraceEnabled()) {
108 						logger.trace("   " + request.getAttribute(Globals.ERROR_KEY));
109 					}
110 				}
111 				return mapping.getInputForward();
112 			}
113 			return forward;
114 		} finally {
115 			if (logger.isTraceEnabled()) {
116 				logger.trace("   forward = " + forward);
117 			}
118 		}
119 	}
120 
121 	/**
122 	 * Implement this method to provide a custom response to a user input.
123 	 * 
124 	 * @param form the html form submitted with this request.
125 	 * @param request the user request.
126 	 * @param response the representation of the response channel.
127 	 * @param mapping the system control mapping.
128 	 * @return the name of a defined global or local forward.
129 	 * @throws Exception if something unexpected happend during the request
130 	 *         execution.
131 	 */
132 	protected abstract ActionForward execute(ActionForm form, HttpServletRequest request, HttpServletResponse response,
133 			ActionMapping mapping) throws Exception;
134 
135 	/**
136 	 * Describes the operations sequence to be performed upon cancellation of a
137 	 * form or wizard returning the mapping to redirect to. By default this
138 	 * method simply does nothing more than redirecting to the
139 	 * <code>input</code> path.
140 	 * 
141 	 * @param form the html form submitted with this request.
142 	 * @param request the user request.
143 	 * @param response the representation of the response channel.
144 	 * @param mapping the system control mapping.
145 	 * @return the name of a defined global or local forward.
146 	 * @throws Exception if something unexpected happend during the request
147 	 *         execution.
148 	 */
149 	protected ActionForward cancel(ActionForm form, HttpServletRequest request, HttpServletResponse response,
150 			ActionMapping mapping) throws Exception {
151 		return DEFAULT_FORWARD;
152 	}
153 
154 	/**
155 	 * Valorizes a <code>bean</code> instance with the values providen with
156 	 * the user submitted html form. This method uses an improved version of the
157 	 * introspection paradigm to discover properties bindings.
158 	 * 
159 	 * @param form the user submitted html form containing the values to be
160 	 *        read.
161 	 * @param bean the bean instance to be valorized.
162 	 * @param locale the user locale for values parsing.
163 	 * @throws ActionException if the valorization fails, usually due to
164 	 *         inconsistencies between the bean and the submitted form.
165 	 */
166 	protected void valorize(ActionForm form, Object bean, Locale locale) throws ActionException {
167 		try {
168 			if (logger.isDebugEnabled()) {
169 				logger.debug("valorize(" + form + ", " + bean + ") - start");
170 			}
171 			// FIXME
172 			// this.getConverter(locale).copyProperties(bean, form);
173 			Valorizer.copy(form, bean, locale);
174 		} catch (Exception e) {
175 			logger.debug("valorize(" + form + ", " + bean + ") - error", e);
176 			throw new ActionException("action.error.valorize", e);
177 		}
178 	}
179 
180 	/**
181 	 * Populates, or prevalorizes, an html form with the values of a
182 	 * <code>bean</code> instance . This method uses an improved version of
183 	 * the introspection paradigm to discover properties bindings.
184 	 * 
185 	 * @param form the form to be populated.
186 	 * @param bean the bean instance containing the values to be written in the
187 	 *        form.
188 	 * @param locale the user locale for values parsing.
189 	 * @throws ActionException if the valorization fails, usually due to
190 	 *         inconsistencies between the bean and the submitted form.
191 	 */
192 	protected void populate(ActionForm form, Object bean, Locale locale) throws ActionException {
193 		try {
194 			if (logger.isDebugEnabled()) {
195 				logger.debug("populate(" + form + ", " + bean + ") - start");
196 			}
197 			// FIXME
198 			// this.getConverter(locale).copyProperties(form,bean);
199 			Valorizer.copy(bean, form, locale);
200 		} catch (Exception e) {
201 			logger.debug("populate(" + form + ", " + bean + ") - error", e);
202 			throw new ActionException("action.error.populate", e);
203 		}
204 	}
205 
206 	/**
207 	 * @TODO documentation
208 	 * @param locale
209 	 * @return
210 	 */
211 	private LocaleBeanUtilsBean getConverter(Locale locale) {
212 		LocaleConvertUtilsBean converter = new LocaleConvertUtilsBean() {
213 			protected FastHashMap create(Locale locale) {
214 				FastHashMap converter = super.create(locale);
215 				converter.setFast(false);
216 				// FIXME add converters
217 				converter.setFast(true);
218 				return converter;
219 			}
220 		};
221 		converter.setDefaultLocale(locale);
222 		return new LocaleBeanUtilsBean(converter);
223 	}
224 
225 	/**
226 	 * Ensures the specified html form has all its fields resetted to their
227 	 * initial value.
228 	 * 
229 	 * @param form the form to be cleaned up.
230 	 * @param request the user request.
231 	 * @param mapping the system control mapping.
232 	 */
233 	public void reset(ActionForm form, HttpServletRequest request, ActionMapping mapping) {
234 		if (form instanceof DynaActionForm) {
235 			((DynaActionForm)form).initialize(mapping);
236 		} else {
237 			form.reset(mapping, request);
238 		}
239 	}
240 
241 	/**
242 	 * TODO documentation
243 	 * 
244 	 * @param request
245 	 * @return
246 	 */
247 	protected boolean hasErrors(HttpServletRequest request) {
248 		return request.getAttribute(Globals.ERROR_KEY) != null;
249 	}
250 
251 	/**
252 	 * TODO documentation
253 	 * 
254 	 * @param message
255 	 * @param request
256 	 */
257 	protected void addError(ActionMessage message, HttpServletRequest request) {
258 		this.addError(ActionMessages.GLOBAL_MESSAGE, message, request);
259 	}
260 
261 	/**
262 	 * TODO documentation
263 	 * 
264 	 * @param property
265 	 * @param message
266 	 * @param request
267 	 */
268 	protected void addError(String property, ActionMessage message, HttpServletRequest request) {
269 		ActionErrors errors = (ActionErrors)request.getAttribute(Globals.ERROR_KEY);
270 		if (errors == null) {
271 			errors = new ActionErrors();
272 			request.setAttribute(Globals.ERROR_KEY, errors);
273 		}
274 		errors.add(property, message);
275 	}
276 
277 	/**
278 	 * TODO documentation
279 	 * 
280 	 * @param name
281 	 * @param request
282 	 * @return
283 	 */
284 	public boolean isChecked(String name, HttpServletRequest request) {
285 		if ("on".equalsIgnoreCase(request.getParameter("group"))
286 				|| "true".equalsIgnoreCase(request.getParameter("group"))
287 				|| "yes".equalsIgnoreCase(request.getParameter("group"))) {
288 			return true;
289 		} else {
290 			return false;
291 		}
292 	}
293 
294 	/**
295 	 * TODO documentation
296 	 * 
297 	 * @return
298 	 */
299 	protected ServletContext getServletContext() {
300 		return super.getServlet().getServletContext();
301 	}
302 
303 	/**
304 	 * TODO documentation
305 	 * 
306 	 * @param path
307 	 * @return
308 	 */
309 	protected String getRealPath(String path) {
310 		return this.getServletContext().getRealPath(path);
311 	}
312 
313 	/**
314 	 * TODO documentation
315 	 * 
316 	 * @return
317 	 */
318 	protected Properties getProperties() {
319 		Class type = this.getClass();
320 		List inputs = new ArrayList();
321 		Properties result = new Properties();
322 		while (type.getSuperclass().isAssignableFrom(Action.class)) {
323 			try {
324 				InputStream in = this.getClass().getResourceAsStream(this.getClass().getName() + ".prop");
325 				inputs.add(in);
326 			} catch (Exception e) {
327 				if (logger.isDebugEnabled()) {
328 					logger.debug("getProperties() - descend", e);
329 				}
330 			}
331 			type = type.getSuperclass();
332 		}
333 		Iterator iterator = inputs.iterator();
334 		while (iterator.hasNext()) {
335 			try {
336 				InputStream in = (InputStream)iterator.next();
337 				Properties properties = new Properties();
338 				properties.load(in);
339 				result.putAll(properties);
340 			} catch (Exception e) {
341 				if (logger.isDebugEnabled()) {
342 					logger.debug("getProperties() - load", e);
343 				}
344 			}
345 		}
346 		throw new MissingResourceException("No properties defined for action", this.getClass().getName(), null);
347 	}
348 }