2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.component.Token
 
Classes in this File Line Coverage Branch Coverage Complexity
Token
100%
67/67
N/A
2.769
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to you under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.shale.component;
 19  
 
 20  
 import javax.faces.application.FacesMessage;
 21  
 import javax.faces.component.UIInput;
 22  
 import javax.faces.context.FacesContext;
 23  
 import javax.faces.el.ValueBinding;
 24  
 
 25  
 import org.apache.commons.logging.Log;
 26  
 import org.apache.commons.logging.LogFactory;
 27  
 import org.apache.shale.faces.ShaleConstants;
 28  
 import org.apache.shale.util.LoadBundle;
 29  
 import org.apache.shale.util.Messages;
 30  
 import org.apache.shale.util.TokenProcessor;
 31  
 
 32  
 /**
 33  
  * <p>Component that renders a transaction token input field, and then
 34  
  * validates it on a subsequent form submit. The token component must
 35  
  * be the last input component child of the parent form to be processed.</p>
 36  
  *
 37  
  * $Id: Token.java 472288 2006-11-07 21:41:16Z rahul $
 38  
  */
 39  
 public class Token extends UIInput {
 40  
 
 41  
 
 42  
     // -------------------------------------------------------- Static Variables
 43  
 
 44  
 
 45  
     /**
 46  
      * <p>Log instance for this class.</p>
 47  
      */
 48  2
     private static final Log log = LogFactory.getLog(Token.class);
 49  
 
 50  
 
 51  
     /**
 52  
      * <p>The resource bundle <code>messageSummary</code> key.</p>
 53  
      */
 54  
     private static final String MESSAGE_SUMMARY_KEY = "token.summary.invalid";
 55  
 
 56  
     /**
 57  
      * <p>The resource bundle <code>messageDetail</code> key.</p>
 58  
      */
 59  
     private static final String MESSAGE_DETAIL_KEY = "token.detail.invalid";
 60  
 
 61  
 
 62  
     /**
 63  
      * <p>Local component attribute under which we store the token value
 64  
      * the first time it is generated.</p>
 65  
      */
 66  
     private static final String TOKEN_ATTRIBUTE_KEY = "org.apache.shale.Token.TOKEN_VALUE";
 67  
 
 68  
 
 69  
     /**
 70  
      * <p>Message resources for this class.</p>
 71  
      */
 72  1
     private static Messages messages =
 73  
       new Messages("org.apache.shale.resources.Bundle",
 74  
                    Token.class.getClassLoader());
 75  
 
 76  
 
 77  
     // ------------------------------------------------------------ Constructors
 78  
 
 79  
     /**
 80  
      * <p>A validation message summary override that can be used to change the default
 81  
      * validation message summary when the token verification fails.</p>
 82  
      */
 83  3
     private String messageSummary = null;
 84  
 
 85  
     /**
 86  
      * <p>Returns the validation <code>messageSummary</code> used to create a
 87  
      * <code>FacesMessage.SEVERITY_ERROR</code>.</p>
 88  
      */
 89  
     public String getMessageSummary() {
 90  3
         if (null != messageSummary) {
 91  1
             return messageSummary;
 92  
         }
 93  2
         ValueBinding valuebinding = getValueBinding("messageSummary");
 94  2
         if (valuebinding != null) {
 95  
             return (String) valuebinding.getValue(getFacesContext());
 96  
         } else {
 97  2
             return null;
 98  
         }
 99  
     }
 100  
 
 101  
     /**
 102  
      * <p>Sets a <code>messageSummary</code> override used when reporting
 103  
      * a token verification failure.</p>
 104  
      *
 105  
      * @param message The new message summary
 106  
      */
 107  
     public void setMessageSummary(String message) {
 108  3
        this.messageSummary = message;
 109  3
     }
 110  
 
 111  
 
 112  
     /**
 113  
      * <p>A validation message detail override that can be used to change the default
 114  
      * validation message detail when the token verification fails.</p>
 115  
      */
 116  3
     private String messageDetail = null;
 117  
 
 118  
     /**
 119  
      * <p>Returns the validation <code>messageDetail</code> used to create a
 120  
      * <code>FacesMessage.SEVERITY_ERROR</code>.</p>
 121  
      */
 122  
     public String getMessageDetail() {
 123  3
         if (null != messageDetail) {
 124  1
             return messageDetail;
 125  
         }
 126  2
         ValueBinding valuebinding = getValueBinding("messageDetail");
 127  2
         if (valuebinding != null) {
 128  
             return (String) valuebinding.getValue(getFacesContext());
 129  
         } else {
 130  2
             return null;
 131  
         }
 132  
     }
 133  
 
 134  
     /**
 135  
      * <p>Sets a <code>messageDetail</code> override used when reporting
 136  
      * a token verification failure.</p>
 137  
      *
 138  
      * @param message The new message detail
 139  
      */
 140  
     public void setMessageDetail(String message) {
 141  2
        this.messageDetail = message;
 142  2
     }
 143  
 
 144  
 
 145  
     /**
 146  
      * <p>Create a default instance of this component.</p>
 147  
      */
 148  3
     public Token() {
 149  3
         setRendererType("org.apache.shale.Token");
 150  3
     }
 151  
 
 152  
 
 153  
     // -------------------------------------------------------------- Properties
 154  
 
 155  
 
 156  
     /**
 157  
      * <p>Return the component family for this component.</p>
 158  
      */
 159  
     public String getFamily() {
 160  18
         return "org.apache.shale.Token";
 161  
     }
 162  
 
 163  
 
 164  
     // --------------------------------------------------------- UIInput Methods
 165  
 
 166  
 
 167  
     /**
 168  
      * <p>Perform superclass validations, then ensure that the specified input
 169  
      * value is acceptable at this point in time.</p>
 170  
      *
 171  
      * @param context <code>FacesContext</code> for the current request
 172  
      */
 173  
     public void validate(FacesContext context) {
 174  
 
 175  
         // If any of the other input components in this form triggered
 176  
         // validation errors, we do NOT want to validate the token component
 177  
         // here, because that would erase the saved token and prevent the
 178  
         // subsequent valid resubmit from succeeding.
 179  
         //
 180  
         // WARNING - for this test to be successful, the token component must
 181  
         // be the last input component child of the parent form to be
 182  
         // processed
 183  6
         if (context.getMaximumSeverity() != null) {
 184  
             return;
 185  
         }
 186  
 
 187  6
         super.validate(context);
 188  6
         String token = (String) getValue();
 189  6
         if (log.isDebugEnabled()) {
 190  
             log.debug("Validating token '" + token + "'");
 191  
         }
 192  6
         TokenProcessor tp = getTokenProcessor(context);
 193  6
         if (!tp.verify(context, token)) {
 194  3
             if (log.isDebugEnabled()) {
 195  
                 log.debug("  Validation failed!");
 196  
             }
 197  3
             setValid(false);
 198  3
             String summary = getErrorSummaryMessage(context);
 199  3
             String detail = getErrorDetailMessage(context);
 200  3
             FacesMessage message = new FacesMessage(summary, detail);
 201  3
             message.setSeverity(FacesMessage.SEVERITY_ERROR);
 202  3
             context.addMessage(getClientId(context), message);
 203  
         }
 204  
 
 205  6
     }
 206  
 
 207  
     /**
 208  
      * <p>Returns the validation summary message.  The validation
 209  
      * <code>messageSummary</code> is evaluated in the following order:</p>
 210  
      * <ol>
 211  
      * <li>The <code>messageSummary</code> property on the {@link Token}
 212  
      *     component</li>
 213  
      * <li>A custom resource bundled registered in
 214  
      *     <code>faces-config.xml</code> using the message key of
 215  
      *     <strong>token.summary.invalid</strong>.
 216  
      * <p><blockquote><pre>
 217  
      *    &lt;application&gt;
 218  
      *       &lt;messageSummary-bundle&gt;org.acme.resources.Bundle&lt;/messageSummary-bundle&gt;
 219  
      *    &lt;/application&gt;
 220  
      *</pre></blockquote></p></li>
 221  
      * <li>The default will be taken from
 222  
      *     <strong>org.apache.shale.resources.Bundle</strong> packaged
 223  
      *     in the core Shale java archive.  The default message summary
 224  
      *     is "<strong>Invalid resubmit of the same form</strong>".</li>
 225  
      *</ol>
 226  
      *
 227  
      * @param context faces context
 228  
      * @return invalid token message
 229  
      */
 230  
     private String getErrorSummaryMessage(FacesContext context) {
 231  
 
 232  3
         String msg = getMessageSummary();
 233  3
         if (msg == null) {
 234  2
             String bundleName = context.getApplication().getMessageBundle();
 235  2
             if (bundleName != null) {
 236  1
                 LoadBundle loadBundle = new LoadBundle(bundleName);
 237  1
                 msg = (String) loadBundle.getMap().get(MESSAGE_SUMMARY_KEY);
 238  
             }
 239  
         }
 240  3
         if (msg == null) {
 241  1
             msg = messages.getMessage(MESSAGE_SUMMARY_KEY);
 242  
         }
 243  3
         return msg;
 244  
 
 245  
     }
 246  
 
 247  
 
 248  
     /**
 249  
      * <p>Returns the validation detail message.  The validation
 250  
      * <code>messageDetail</code> is evaluated in the following order:</p>
 251  
      * <ol>
 252  
      * <li>The <code>messageDetail</code> property on the {@link Token}
 253  
      *     component</li>
 254  
      * <li>A custom resource bundled registered in
 255  
      *     <code>faces-config.xml</code> using the message key of
 256  
      *     <strong>token.detail.invalid</strong>.
 257  
      * <p><blockquote><pre>
 258  
      *    &lt;application&gt;
 259  
      *       &lt;messageSummary-bundle&gt;org.acme.resources.Bundle&lt;/messageSummary-bundle&gt;
 260  
      *    &lt;/application&gt;
 261  
      * </pre></blockquote></p></li>
 262  
      * <li>The default will be taken from
 263  
      *     <strong>org.apache.shale.resources.Bundle</strong>
 264  
      *     packaged in the core Shale java archive.</li>
 265  
      * <li>The default message detail is
 266  
      *     an empty string, ""</li>
 267  
      *</ol>
 268  
      *
 269  
      * @param context faces context
 270  
      * @return invalid token message
 271  
      */
 272  
     private String getErrorDetailMessage(FacesContext context) {
 273  
 
 274  3
         String msg = getMessageDetail();
 275  3
         if (msg == null) {
 276  2
             String bundleName = context.getApplication().getMessageBundle();
 277  2
             if (bundleName != null) {
 278  1
                 LoadBundle loadBundle = new LoadBundle(bundleName);
 279  1
                 msg = (String) loadBundle.getMap().get(MESSAGE_DETAIL_KEY);
 280  
             }
 281  
         }
 282  3
         if (msg == null) {
 283  1
             msg = messages.getMessage(MESSAGE_DETAIL_KEY);
 284  
         }
 285  3
         return msg;
 286  
 
 287  
     }
 288  
 
 289  
 
 290  
     // ---------------------------------------------------------- Public Methods
 291  
 
 292  
 
 293  
     /**
 294  
      * <p>Return the transaction token value to be rendered for this occcurrence
 295  
      * of this component.  As a side effect, the transaction token value will
 296  
      * be saved for verification on a subsequent submit.</p>
 297  
      */
 298  
     public String getToken() {
 299  
 
 300  
         // Have we already generated a token value?  If so, use it
 301  3
         String value = (String) getAttributes().get(TOKEN_ATTRIBUTE_KEY);
 302  3
         if (value != null) {
 303  
              return value;
 304  
         }
 305  
 
 306  
         // Generate a new token value and cache it for reuse if the
 307  
         // current view is rerendered
 308  3
         FacesContext context = FacesContext.getCurrentInstance();
 309  3
         TokenProcessor tp = getTokenProcessor(context);
 310  3
         String token = tp.generate(context);
 311  3
         getAttributes().put(TOKEN_ATTRIBUTE_KEY, token);
 312  3
         if (log.isDebugEnabled()) {
 313  
             log.debug("Generating token '" + token + "'");
 314  
         }
 315  3
         return token;
 316  
 
 317  
     }
 318  
 
 319  
 
 320  
     // --------------------------------------------------------- Private Methods
 321  
 
 322  
 
 323  
     /**
 324  
      * <p>Retrieve the {@link TokenProcessor} instance for this application,
 325  
      * creating and caching a new one if necessary.</p>
 326  
      *
 327  
      * @param context <code>FacesContext</code> for the current request
 328  
      */
 329  
      private TokenProcessor getTokenProcessor(FacesContext context) {
 330  
 
 331  9
          TokenProcessor tp = (TokenProcessor) context.getExternalContext().
 332  
            getApplicationMap().get(ShaleConstants.TOKEN_PROCESSOR);
 333  9
          if (tp == null) {
 334  3
              tp = new TokenProcessor();
 335  3
              context.getExternalContext().
 336  
                getApplicationMap().put(ShaleConstants.TOKEN_PROCESSOR, tp);
 337  
          }
 338  9
          return tp;
 339  
 
 340  
      }
 341  
 
 342  
     /**
 343  
      * <p>Restores the components state.</p>
 344  
      *
 345  
      * @param context FacesContext for the current request
 346  
      * @param obj State to be restored
 347  
      */
 348  
     public void restoreState(FacesContext context, Object obj) {
 349  
         Object[] aobj = (Object[]) obj;
 350  
         super.restoreState(context, aobj[0]);
 351  
 
 352  
         messageSummary = ((String) aobj[1]);
 353  
         messageDetail = ((String) aobj[2]);
 354  
     }
 355  
 
 356  
     /**
 357  
      * <p>Saves the components state.</p>
 358  
      *
 359  
      * @param context FacesContext for the current request
 360  
      */
 361  
     public Object saveState(FacesContext context) {
 362  
         Object[] aobj = new Object[3];
 363  
         aobj[0] = super.saveState(context);
 364  
         aobj[1] = messageSummary;
 365  
         aobj[2] = messageDetail;
 366  
 
 367  
         return aobj;
 368  
     }
 369  
 
 370  
 
 371  
 }