1 package org.apache.turbine.modules;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.Iterator;
25
26 import org.apache.commons.lang.StringUtils;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 import org.apache.turbine.Turbine;
32 import org.apache.turbine.TurbineConstants;
33 import org.apache.turbine.util.RunData;
34 import org.apache.turbine.util.parser.ParameterParser;
35 import org.apache.turbine.util.parser.ParserUtils;
36
37 /***
38 * <p>
39 *
40 * This is an alternative to the Action class that allows you to do
41 * event based actions. Essentially, you label all your submit buttons
42 * with the prefix of "eventSubmit_" and the suffix of "methodName".
43 * For example, "eventSubmit_doDelete". Then any class that subclasses
44 * this class will get its "doDelete(RunData data)" method executed.
45 * If for any reason, it was not able to execute the method, it will
46 * fall back to executing the doPeform() method which is required to
47 * be implemented.
48 *
49 * <p>
50 *
51 * Limitations:
52 *
53 * <p>
54 *
55 * Because ParameterParser makes all the key values lowercase, we have
56 * to do some work to format the string into a method name. For
57 * example, a button name eventSubmit_doDelete gets converted into
58 * eventsubmit_dodelete. Thus, we need to form some sort of naming
59 * convention so that dodelete can be turned into doDelete.
60 *
61 * <p>
62 *
63 * Thus, the convention is this:
64 *
65 * <ul>
66 * <li>The variable name MUST have the prefix "eventSubmit_".</li>
67 * <li>The variable name after the prefix MUST begin with the letters
68 * "do".</li>
69 * <li>The first letter after the "do" will be capitalized and the
70 * rest will be lowercase</li>
71 * </ul>
72 *
73 * If you follow these conventions, then you should be ok with your
74 * method naming in your Action class.
75 *
76 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens </a>
77 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
78 * @author <a href="quintonm@bellsouth.net">Quinton McCombs</a>
79 * @version $Id: ActionEvent.java 534527 2007-05-02 16:10:59Z tv $
80 */
81 public abstract class ActionEvent extends Action
82 {
83 /*** Logging */
84 protected Log log = LogFactory.getLog(this.getClass());
85
86 /*** Constant needed for Reflection */
87 private static final Class [] methodParams
88 = new Class [] { RunData.class };
89
90 /***
91 * You need to implement this in your classes that extend this class.
92 *
93 * @param data Turbine information.
94 * @exception Exception a generic exception.
95 */
96 public abstract void doPerform(RunData data)
97 throws Exception;
98
99 /*** The name of the button to look for. */
100 protected static final String BUTTON = "eventSubmit_";
101 /*** The length of the button to look for. */
102 protected static final int BUTTON_LENGTH = BUTTON.length();
103 /*** The prefix of the method name. */
104 protected static final String METHOD_NAME_PREFIX = "do";
105 /*** The length of the method name. */
106 protected static final int METHOD_NAME_LENGTH = METHOD_NAME_PREFIX.length();
107 /*** The length of the button to look for. */
108 protected static final int LENGTH = BUTTON.length();
109
110 /***
111 * If true, the eventSubmit_do<xxx> variable must contain
112 * a not null value to be executed.
113 */
114 private boolean submitValueKey = false;
115
116 /***
117 * C'tor
118 */
119 public ActionEvent()
120 {
121 super();
122
123 submitValueKey = Turbine.getConfiguration()
124 .getBoolean(TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_KEY,
125 TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_DEFAULT);
126
127 log.debug(submitValueKey
128 ? "ActionEvent accepts only eventSubmit_do Keys with a value != 0"
129 : "ActionEvent accepts all eventSubmit_do Keys");
130 }
131
132 /***
133 * This overrides the default Action.perform() to execute the
134 * doEvent() method. If that fails, then it will execute the
135 * doPerform() method instead.
136 *
137 * @param data Turbine information.
138 * @exception Exception a generic exception.
139 */
140 protected void perform(RunData data)
141 throws Exception
142 {
143 try
144 {
145 executeEvents(data);
146 }
147 catch (NoSuchMethodException e)
148 {
149 doPerform(data);
150 }
151 }
152
153 /***
154 * This method should be called to execute the event based system.
155 *
156 * @param data Turbine information.
157 * @exception Exception a generic exception.
158 */
159 public void executeEvents(RunData data)
160 throws Exception
161 {
162
163 String theButton = null;
164
165 ParameterParser pp = data.getParameters();
166
167 String button = pp.convert(BUTTON);
168 String key = null;
169
170
171 for (Iterator it = pp.keySet().iterator(); it.hasNext();)
172 {
173 key = (String) it.next();
174 if (key.startsWith(button))
175 {
176 if (considerKey(key, pp))
177 {
178 theButton = formatString(key);
179 break;
180 }
181 }
182 }
183
184 if (theButton == null)
185 {
186 throw new NoSuchMethodException("ActionEvent: The button was null");
187 }
188
189 try
190 {
191 Method method = getClass().getMethod(theButton, methodParams);
192 Object[] methodArgs = new Object[] { data };
193
194 if (log.isDebugEnabled())
195 {
196 log.debug("Invoking " + method);
197 }
198
199 method.invoke(this, methodArgs);
200 }
201 catch (InvocationTargetException ite)
202 {
203 Throwable t = ite.getTargetException();
204 if (t instanceof Exception)
205 {
206 throw (Exception) t;
207 }
208 else
209 {
210 throw ite;
211 }
212 }
213 finally
214 {
215 pp.remove(key);
216 }
217 }
218
219 /***
220 * This method does the conversion of the lowercase method name
221 * into the proper case.
222 *
223 * @param input The unconverted method name.
224 * @return A string with the method name in the proper case.
225 */
226 protected final String formatString(String input)
227 {
228 String tmp = input;
229
230 if (StringUtils.isNotEmpty(input))
231 {
232 tmp = input.toLowerCase();
233
234
235 input = (tmp.endsWith(".x") || tmp.endsWith(".y"))
236 ? input.substring(0, input.length() - 2)
237 : input;
238
239 if (ParserUtils.getUrlFolding()
240 != ParserUtils.URL_CASE_FOLDING_NONE)
241 {
242 tmp = input.toLowerCase().substring(BUTTON_LENGTH + METHOD_NAME_LENGTH);
243 tmp = METHOD_NAME_PREFIX + StringUtils.capitalize(tmp);
244 }
245 else
246 {
247 tmp = input.substring(BUTTON_LENGTH);
248 }
249 }
250 return tmp;
251 }
252
253 /***
254 * Checks whether the selected key really is a valid event.
255 *
256 * @param key The selected key
257 * @param pp The parameter parser to look for the key value
258 *
259 * @return true if this key is really an ActionEvent Key
260 */
261 protected boolean considerKey(String key, ParameterParser pp)
262 {
263 if (!submitValueKey)
264 {
265 log.debug("No Value required, accepting " + key);
266 return true;
267 }
268 else
269 {
270
271
272
273
274
275
276
277
278
279 String keyValue = pp.getString(key);
280 log.debug("Key Value is " + keyValue);
281 if (StringUtils.isEmpty(keyValue))
282 {
283 log.debug("Key is empty, rejecting " + key);
284 return false;
285 }
286
287 try
288 {
289 if (Integer.parseInt(keyValue) != 0)
290 {
291 log.debug("Integer != 0, accepting " + key);
292 return true;
293 }
294 }
295 catch (NumberFormatException nfe)
296 {
297
298
299
300 log.debug("Not a number, accepting " + key);
301 return true;
302 }
303 }
304 log.debug("Rejecting " + key);
305 return false;
306 }
307 }