Coverage Report - org.apache.turbine.modules.ActionEvent
 
Classes in this File Line Coverage Branch Coverage Complexity
ActionEvent
23%
24/102
22%
9/40
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  
 }