##### Coverage Report - org.apache.turbine.modules.ActionEvent

Classes in this File | Line Coverage | Branch Coverage | Complexity
ActionEvent |
23% | 24/102
22% | 9/40
| 4.444444444444445;4,444

1 |  |
     package org.apache.turbine.modules;
2 |  |
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 |  |
     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 |  |
     import org.apache.commons.logging.Log;
28 |  |
     import org.apache.commons.logging.LogFactory;
29 |  |
     import org.apache.fulcrum.parser.ParameterParser;
30 |  |
     import org.apache.fulcrum.parser.ParserService;
31 |  |
     import org.apache.turbine.Turbine;
32 |  |
     import org.apache.turbine.TurbineConstants;
33 |  |
     import org.apache.turbine.pipeline.PipelineData;
34 |  |
     import org.apache.turbine.util.RunData;
35 |  |
36 |  |
     /**
37 |  |
      * <p>
38 |  |
      *
39 |  |
      * This is an alternative to the Action class that allows you to do
40 |  |
      * event based actions. Essentially, you label all your submit buttons
41 |  |
      * with the prefix of "eventSubmit_" and the suffix of "methodName".
42 |  |
      * For example, "eventSubmit_doDelete". Then any class that subclasses
43 |  |
      * this class will get its "doDelete(RunData data)" method executed.
44 |  |
      * If for any reason, it was not able to execute the method, it will
45 |  |
      * fall back to executing the doPeform() method which is required to
46 |  |
      * be implemented.
47 |  |
      *
48 |  |
      * <p>
49 |  |
      *
50 |  |
      * Limitations:
51 |  |
      *
52 |  |
      * <p>
53 |  |
      *
54 |  |
      * Because ParameterParser makes all the key values lowercase, we have
55 |  |
      * to do some work to format the string into a method name. For
56 |  |
      * example, a button name eventSubmit_doDelete gets converted into
57 |  |
      * eventsubmit_dodelete. Thus, we need to form some sort of naming
58 |  |
      * convention so that dodelete can be turned into doDelete.
59 |  |
      *
60 |  |
      * <p>
61 |  |
      *
62 |  |
      * Thus, the convention is this:
63 |  |
      *
64 |  |
      * <ul>
65 |  |
      * <li>The variable name MUST have the prefix "eventSubmit_".</li>
66 |  |
      * <li>The variable name after the prefix MUST begin with the letters
67 |  |
      * "do".</li>
68 |  |
      * <li>The first letter after the "do" will be capitalized and the
69 |  |
      * rest will be lowercase</li>
70 |  |
      * </ul>
71 |  |
      *
72 |  |
      * If you follow these conventions, then you should be ok with your
73 |  |
      * method naming in your Action class.
74 |  |
      *
75 |  |
      * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens </a>
76 |  |
      * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
77 |  |
      * @author <a href="quintonm@bellsouth.net">Quinton McCombs</a>
78 |  |
      * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
79 |  |
      * @version $Id: ActionEvent.java 1078552 2011-03-06 19:58:46Z tv $
80 |  |
      */
81 |  |
     public abstract class ActionEvent extends Action
82 |  |
     {
83 |  |
             /** Logging */
84 | 22 |
             protected Log log = LogFactory.getLog(this.getClass());
85 |  |
86 |  |
             /** Constant needed for Reflection */
87 | 4 |
             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 |  |
              * @deprecated use PipelineData version instead.
93 |  |
              * @param data Turbine information.
94 |  |
              * @exception Exception a generic exception.
95 |  |
              */
96 |  |
             @Deprecated
97 |  |
         @Override
98 |  |
         public abstract void doPerform(RunData data)
99 |  |
                             throws Exception;
100 |  |
101 |  |
             /**
102 |  |
              * You need to implement this in your classes that extend this class.
103 |  |
              * This should revert to being abstract when RunData has gone.
104 |  |
              * @param data Turbine information.
105 |  |
              * @exception Exception a generic exception.
106 |  |
              */
107 |  |
             @Override
108 |  |
         public void doPerform(PipelineData pipelineData)
109 |  |
                             throws Exception
110 |  |
             {
111 | 0 |
                   RunData data = getRunData(pipelineData);
112 | 0 |
                   doPerform(data);
113 | 0 |
             }
114 |  |
115 |  |
116 |  |
             /** The name of the button to look for. */
117 |  |
             protected static final String BUTTON = "eventSubmit_";
118 |  |
             /** The length of the button to look for. */
119 | 4 |
             protected static final int BUTTON_LENGTH = BUTTON.length();
120 |  |
             /** The prefix of the method name. */
121 |  |
             protected static final String METHOD_NAME_PREFIX = "do";
122 |  |
             /** The length of the method name. */
123 | 4 |
             protected static final int METHOD_NAME_LENGTH = METHOD_NAME_PREFIX.length();
124 |  |
             /** The length of the button to look for. */
125 | 4 |
             protected static final int LENGTH = BUTTON.length();
126 |  |
127 |  |
             /**
128 |  |
              * If true, the eventSubmit_do<xxx> variable must contain
129 |  |
              * a not null value to be executed.
130 |  |
              */
131 | 22 |
             private boolean submitValueKey = false;
132 |  |
133 |  |
             /**
134 |  |
              * If true, then exceptions raised in eventSubmit_do<xxx> methods
135 |  |
              * as well as in doPerform methods are bubbled up to the Turbine
136 |  |
              * servlet's handleException method.
137 |  |
              */
138 | 22 |
             protected boolean bubbleUpException = true;
139 |  |
             /**
140 |  |
              * C'tor
141 |  |
              */
142 |  |
             public ActionEvent()
143 |  |
             {
144 | 22 |
                     super();
145 |  |
146 | 22 |
                     submitValueKey = Turbine.getConfiguration()
147 |  |
                                     .getBoolean(TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_KEY,
148 |  |
                                                     TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_DEFAULT);
149 | 22 |
                     bubbleUpException = Turbine.getConfiguration()
150 |  |
                                     .getBoolean(TurbineConstants.ACTION_EVENT_BUBBLE_EXCEPTION_UP,
151 |  |
                                                     TurbineConstants.ACTION_EVENT_BUBBLE_EXCEPTION_UP_DEFAULT);
152 |  |
153 | 22 |
                     if (log.isDebugEnabled()){
154 | 22 |
                     log.debug(submitValueKey
155 |  |
                                     ? "ActionEvent accepts only eventSubmit_do Keys with a value != 0"
156 |  |
                                     : "ActionEvent accepts all eventSubmit_do Keys");
157 | 22 |
                     log.debug(bubbleUpException
158 |  |
                                       ? "ActionEvent will bubble exceptions up to Turbine.handleException() method"
159 |  |
                                       : "ActionEvent will not bubble exceptions up.");
160 |  |
                     }
161 | 22 |
             }
162 |  |
163 |  |
             /**
164 |  |
              * This overrides the default Action.perform() to execute the
165 |  |
              * doEvent() method. If that fails, then it will execute the
166 |  |
              * doPerform() method instead.
167 |  |
              * @deprecated Use PipelineData version instead.
168 |  |
              * @param data Turbine information.
169 |  |
              * @exception Exception a generic exception.
170 |  |
              */
171 |  |
             @Deprecated
172 |  |
         @Override
173 |  |
         protected void perform(RunData data)
174 |  |
                             throws Exception
175 |  |
             {
176 |  |
                     try
177 |  |
                     {
178 | 0 |
                             executeEvents(data);
179 |  |
                     }
180 | 0 |
                     catch (NoSuchMethodException e)
181 |  |
                     {
182 | 0 |
                             doPerform(data);
183 | 0 |
                     }
184 | 0 |
             }
185 |  |
186 |  |
             /**
187 |  |
              * This overrides the default Action.perform() to execute the
188 |  |
              * doEvent() method. If that fails, then it will execute the
189 |  |
              * doPerform() method instead.
190 |  |
              *
191 |  |
              * @param data Turbine information.
192 |  |
              * @exception Exception a generic exception.
193 |  |
              */
194 |  |
             @Override
195 |  |
         protected void perform(PipelineData pipelineData)
196 |  |
                             throws Exception
197 |  |
             {
198 |  |
                     try
199 |  |
                     {
200 | 0 |
                             executeEvents(pipelineData);
201 |  |
                     }
202 | 0 |
                     catch (NoSuchMethodException e)
203 |  |
                     {
204 | 0 |
                             doPerform(pipelineData);
205 | 0 |
                     }
206 | 0 |
             }
207 |  |
208 |  |
209 |  |
             /**
210 |  |
              * This method should be called to execute the event based system.
211 |  |
              *
212 |  |
              * @deprecated Use PipelineData version instead.
213 |  |
              * @param data Turbine information.
214 |  |
              * @exception Exception a generic exception.
215 |  |
              */
216 |  |
             @Deprecated
217 |  |
         public void executeEvents(RunData data)
218 |  |
                             throws Exception
219 |  |
             {
220 |  |
                     // Name of the button.
221 | 0 |
                     String theButton = null;
222 |  |
                     // Parameter parser.
223 | 0 |
                     ParameterParser pp = data.getParameters();
224 |  |
225 | 0 |
                     String button = pp.convert(BUTTON);
226 | 0 |
                     String key = null;
227 |  |
228 |  |
                     // Loop through and find the button.
229 | 0 |
                     for (Iterator it = pp.keySet().iterator(); it.hasNext();)
230 |  |
                     {
231 | 0 |
                             key = (String) it.next();
232 | 0 |
                             if (key.startsWith(button))
233 |  |
                             {
234 | 0 |
                                     if (considerKey(key, pp))
235 |  |
                                     {
236 | 0 |
                                             theButton = formatString(key, pp);
237 | 0 |
                                             break;
238 |  |
                                     }
239 |  |
                             }
240 |  |
                     }
241 |  |
242 | 0 |
                     if (theButton == null)
243 |  |
                     {
244 | 0 |
                             throw new NoSuchMethodException("ActionEvent: The button was null");
245 |  |
                     }
246 |  |
247 | 0 |
                     Method method = null;
248 |  |
249 |  |
                     try
250 |  |
                     {
251 | 0 |
                             method = getClass().getMethod(theButton, methodParams);
252 | 0 |
                             Object[] methodArgs = new Object[] { data };
253 |  |
254 | 0 |
                             if (log.isDebugEnabled())
255 |  |
                             {
256 | 0 |
                                     log.debug("Invoking " + method);
257 |  |
                             }
258 |  |
259 | 0 |
                             method.invoke(this, methodArgs);
260 |  |
                     }
261 | 0 |
                     catch (InvocationTargetException ite)
262 |  |
                     {
263 | 0 |
                             Throwable t = ite.getTargetException();
264 | 0 |
                             log.error("Invokation of " + method , t);
265 |  |
                     }
266 |  |
                     finally
267 |  |
                     {
268 | 0 |
                             pp.remove(key);
269 | 0 |
                     }
270 | 0 |
             }
271 |  |
272 |  |
             /**
273 |  |
              * This method should be called to execute the event based system.
274 |  |
              *
275 |  |
              * @param data Turbine information.
276 |  |
              * @exception Exception a generic exception.
277 |  |
              */
278 |  |
             public void executeEvents(PipelineData pipelineData)
279 |  |
                             throws Exception
280 |  |
             {
281 |  |
282 | 0 |
                 RunData data = getRunData(pipelineData);
283 |  |
284 |  |
                     // Name of the button.
285 | 0 |
                     String theButton = null;
286 |  |
                     // Parameter parser.
287 | 0 |
                     ParameterParser pp = data.getParameters();
288 |  |
289 | 0 |
                     String button = pp.convert(BUTTON);
290 | 0 |
                     String key = null;
291 |  |
292 |  |
                     // Loop through and find the button.
293 | 0 |
                     for (Iterator it = pp.keySet().iterator(); it.hasNext();)
294 |  |
                     {
295 | 0 |
                             key = (String) it.next();
296 | 0 |
                             if (key.startsWith(button))
297 |  |
                             {
298 | 0 |
                                     if (considerKey(key, pp))
299 |  |
                                     {
300 | 0 |
                                             theButton = formatString(key, pp);
301 | 0 |
                                             break;
302 |  |
                                     }
303 |  |
                             }
304 |  |
                     }
305 |  |
306 | 0 |
                     if (theButton == null)
307 |  |
                     {
308 | 0 |
                             throw new NoSuchMethodException("ActionEvent: The button was null");
309 |  |
                     }
310 |  |
311 | 0 |
                     Method method = null;
312 |  |
313 |  |
                     try
314 |  |
                     {
315 | 0 |
                             method = getClass().getMethod(theButton, methodParams);
316 | 0 |
                             Object[] methodArgs = new Object[] { pipelineData };
317 |  |
318 | 0 |
                             if (log.isDebugEnabled())
319 |  |
                             {
320 | 0 |
                                     log.debug("Invoking " + method);
321 |  |
                             }
322 |  |
323 | 0 |
                             method.invoke(this, methodArgs);
324 |  |
                     }
325 | 0 |
                     catch (InvocationTargetException ite)
326 |  |
                     {
327 | 0 |
                             Throwable t = ite.getTargetException();
328 | 0 |
                             log.error("Invokation of " + method , t);
329 |  |
                     }
330 |  |
                     finally
331 |  |
                     {
332 | 0 |
                             pp.remove(key);
333 | 0 |
                     }
334 | 0 |
             }
335 |  |
336 |  |
337 |  |
338 |  |
             /**
339 |  |
              * This method does the conversion of the lowercase method name
340 |  |
              * into the proper case.
341 |  |
              *
342 |  |
              * @param input The unconverted method name.
343 |  |
              * @param pp The parameter parser (for correct folding)
344 |  |
              * @return A string with the method name in the proper case.
345 |  |
              */
346 |  |
             protected final String formatString(String input, ParameterParser pp)
347 |  |
             {
348 | 4 |
                     String tmp = input;
349 |  |
350 | 4 |
                     if (StringUtils.isNotEmpty(input))
351 |  |
                     {
352 | 4 |
                             tmp = input.toLowerCase();
353 |  |
354 |  |
                             // Chop off suffixes (for image type)
355 | 4 |
                             input = (tmp.endsWith(".x") || tmp.endsWith(".y"))
356 |  |
                                             ? input.substring(0, input.length() - 2)
357 |  |
                                             : input;
358 |  |
359 | 4 |
                             if (pp.getUrlFolding()
360 |  |
                                             != ParserService.URL_CASE_FOLDING_NONE)
361 |  |
                             {
362 | 0 |
                                     tmp = input.toLowerCase().substring(BUTTON_LENGTH + METHOD_NAME_LENGTH);
363 | 0 |
                                     tmp = METHOD_NAME_PREFIX + StringUtils.capitalize(tmp);
364 |  |
                             }
365 |  |
                             else
366 |  |
                             {
367 | 4 |
                                     tmp = input.substring(BUTTON_LENGTH);
368 |  |
                             }
369 |  |
                     }
370 | 4 |
                     return tmp;
371 |  |
             }
372 |  |
373 |  |
             /**
374 |  |
              * Checks whether the selected key really is a valid event.
375 |  |
              *
376 |  |
              * @param key The selected key
377 |  |
              * @param pp The parameter parser to look for the key value
378 |  |
              *
379 |  |
              * @return true if this key is really an ActionEvent Key
380 |  |
              */
381 |  |
             protected boolean considerKey(String key, ParameterParser pp)
382 |  |
             {
383 | 4 |
                     if (!submitValueKey)
384 |  |
                     {
385 | 4 |
                             log.debug("No Value required, accepting " + key);
386 | 4 |
                             return true;
387 |  |
                     }
388 |  |
                     else
389 |  |
                     {
390 |  |
                             // If the action.eventsubmit.needsvalue key is true,
391 |  |
                             // events with a "0" or empty value are ignored.
392 |  |
                             // This can be used if you have multiple eventSubmit_do<xxx>
393 |  |
                             // fields in your form which are selected by client side code,
394 |  |
                             // e.g. JavaScript.
395 |  |
                             //
396 |  |
                             // If this key is unset or missing, nothing changes for the
397 |  |
                             // current behaviour.
398 |  |
                             //
399 | 0 |
                             String keyValue = pp.getString(key);
400 | 0 |
                             log.debug("Key Value is " + keyValue);
401 | 0 |
                             if (StringUtils.isEmpty(keyValue))
402 |  |
                             {
403 | 0 |
                                     log.debug("Key is empty, rejecting " + key);
404 | 0 |
                                     return false;
405 |  |
                             }
406 |  |
407 |  |
                             try
408 |  |
                             {
409 | 0 |
                                     if (Integer.parseInt(keyValue) != 0)
410 |  |
                                     {
411 | 0 |
                                             log.debug("Integer != 0, accepting " + key);
412 | 0 |
                                             return true;
413 |  |
                                     }
414 |  |
                             }
415 | 0 |
                             catch (NumberFormatException nfe)
416 |  |
                             {
417 |  |
                                     // Not a number. So it might be a
418 |  |
                                     // normal Key like "continue" or "exit". Accept
419 |  |
                                     // it.
420 | 0 |
                                     log.debug("Not a number, accepting " + key);
421 | 0 |
                                     return true;
422 | 0 |
                             }
423 |  |
                     }
424 | 0 |
                     log.debug("Rejecting " + key);
425 | 0 |
                     return false;
426 |  |
             }
427 |  |
     }