2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
43
44
45 /***
46 * <p>Log instance for this class.</p>
47 */
48 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 private static Messages messages =
73 new Messages("org.apache.shale.resources.Bundle",
74 Token.class.getClassLoader());
75
76
77
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 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 if (null != messageSummary) {
91 return messageSummary;
92 }
93 ValueBinding valuebinding = getValueBinding("messageSummary");
94 if (valuebinding != null) {
95 return (String) valuebinding.getValue(getFacesContext());
96 } else {
97 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 this.messageSummary = message;
109 }
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 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 if (null != messageDetail) {
124 return messageDetail;
125 }
126 ValueBinding valuebinding = getValueBinding("messageDetail");
127 if (valuebinding != null) {
128 return (String) valuebinding.getValue(getFacesContext());
129 } else {
130 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 this.messageDetail = message;
142 }
143
144
145 /***
146 * <p>Create a default instance of this component.</p>
147 */
148 public Token() {
149 setRendererType("org.apache.shale.Token");
150 }
151
152
153
154
155
156 /***
157 * <p>Return the component family for this component.</p>
158 */
159 public String getFamily() {
160 return "org.apache.shale.Token";
161 }
162
163
164
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
176
177
178
179
180
181
182
183 if (context.getMaximumSeverity() != null) {
184 return;
185 }
186
187 super.validate(context);
188 String token = (String) getValue();
189 if (log.isDebugEnabled()) {
190 log.debug("Validating token '" + token + "'");
191 }
192 TokenProcessor tp = getTokenProcessor(context);
193 if (!tp.verify(context, token)) {
194 if (log.isDebugEnabled()) {
195 log.debug(" Validation failed!");
196 }
197 setValid(false);
198 String summary = getErrorSummaryMessage(context);
199 String detail = getErrorDetailMessage(context);
200 FacesMessage message = new FacesMessage(summary, detail);
201 message.setSeverity(FacesMessage.SEVERITY_ERROR);
202 context.addMessage(getClientId(context), message);
203 }
204
205 }
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 * <application>
218 * <messageSummary-bundle>org.acme.resources.Bundle</messageSummary-bundle>
219 * </application>
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 String msg = getMessageSummary();
233 if (msg == null) {
234 String bundleName = context.getApplication().getMessageBundle();
235 if (bundleName != null) {
236 LoadBundle loadBundle = new LoadBundle(bundleName);
237 msg = (String) loadBundle.getMap().get(MESSAGE_SUMMARY_KEY);
238 }
239 }
240 if (msg == null) {
241 msg = messages.getMessage(MESSAGE_SUMMARY_KEY);
242 }
243 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 * <application>
259 * <messageSummary-bundle>org.acme.resources.Bundle</messageSummary-bundle>
260 * </application>
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 String msg = getMessageDetail();
275 if (msg == null) {
276 String bundleName = context.getApplication().getMessageBundle();
277 if (bundleName != null) {
278 LoadBundle loadBundle = new LoadBundle(bundleName);
279 msg = (String) loadBundle.getMap().get(MESSAGE_DETAIL_KEY);
280 }
281 }
282 if (msg == null) {
283 msg = messages.getMessage(MESSAGE_DETAIL_KEY);
284 }
285 return msg;
286
287 }
288
289
290
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
301 String value = (String) getAttributes().get(TOKEN_ATTRIBUTE_KEY);
302 if (value != null) {
303 return value;
304 }
305
306
307
308 FacesContext context = FacesContext.getCurrentInstance();
309 TokenProcessor tp = getTokenProcessor(context);
310 String token = tp.generate(context);
311 getAttributes().put(TOKEN_ATTRIBUTE_KEY, token);
312 if (log.isDebugEnabled()) {
313 log.debug("Generating token '" + token + "'");
314 }
315 return token;
316
317 }
318
319
320
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 TokenProcessor tp = (TokenProcessor) context.getExternalContext().
332 getApplicationMap().get(ShaleConstants.TOKEN_PROCESSOR);
333 if (tp == null) {
334 tp = new TokenProcessor();
335 context.getExternalContext().
336 getApplicationMap().put(ShaleConstants.TOKEN_PROCESSOR, tp);
337 }
338 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 }