001: /*
002: * $Id: ActionContextBase.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts.chain.contexts;
022:
023: import org.apache.commons.chain.Context;
024: import org.apache.commons.chain.impl.ContextBase;
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.apache.struts.action.Action;
028: import org.apache.struts.action.ActionForm;
029: import org.apache.struts.action.ActionMessages;
030: import org.apache.struts.chain.Constants;
031: import org.apache.struts.config.ActionConfig;
032: import org.apache.struts.config.FormBeanConfig;
033: import org.apache.struts.config.ForwardConfig;
034: import org.apache.struts.config.ModuleConfig;
035: import org.apache.struts.util.MessageResources;
036: import org.apache.struts.util.TokenProcessor;
037:
038: import java.util.Locale;
039: import java.util.Map;
040:
041: /**
042: * <p> Provide an abstract but semi-complete implementation of ActionContext
043: * to serve as the base for concrete implementations. </p> <p> The abstract
044: * methods to implement are the accessors for the named states,
045: * <code>getApplicationScope</code>, <code>getRequestScope</code>, and
046: * <code>getSessionScope</code>. </p>
047: */
048: public abstract class ActionContextBase extends ContextWrapper
049: implements ActionContext {
050: /**
051: * @see Constants.ACTION_KEY
052: */
053: public static final String ACTION_KEY = Constants.ACTION_KEY;
054:
055: /**
056: * @see
057: */
058: public static final String ACTION_CONFIG_KEY = Constants.ACTION_CONFIG_KEY;
059:
060: /**
061: * @see Constants.ACTION_FORM_KEY
062: */
063: public static final String ACTION_FORM_KEY = Constants.ACTION_FORM_KEY;
064:
065: /**
066: * @see Constants.FORWARD_CONFIG_KEY
067: */
068: public static final String FORWARD_CONFIG_KEY = Constants.FORWARD_CONFIG_KEY;
069:
070: /**
071: * @see Constants.MODULE_CONFIG_KEY
072: */
073: public static final String MODULE_CONFIG_KEY = Constants.MODULE_CONFIG_KEY;
074:
075: /**
076: * @see Constants.EXCEPTION_KEY
077: */
078: public static final String EXCEPTION_KEY = Constants.EXCEPTION_KEY;
079:
080: /**
081: * <p> Provide the default context attribute under which to store the
082: * ActionMessage cache for errors. </p>
083: */
084: public static final String ERROR_ACTION_MESSAGES_KEY = "errors";
085:
086: /**
087: * <p> Provide the default context attribute under which to store the
088: * ActionMessage cache. </p>
089: */
090: public static final String MESSAGE_ACTION_MESSAGES_KEY = "messages";
091:
092: /**
093: * @see Constants.MESSAGE_RESOURCES_KEY
094: */
095: public static final String MESSAGE_RESOURCES_KEY = Constants.MESSAGE_RESOURCES_KEY;
096:
097: /**
098: * @see Constants.INCLUDE_KEY
099: */
100: public static final String INCLUDE_KEY = Constants.INCLUDE_KEY;
101:
102: /**
103: * @see Constants.LOCALE_KEY
104: */
105: public static final String LOCALE_KEY = Constants.LOCALE_KEY;
106:
107: /**
108: * @see Constants.CANCEL_KEY
109: */
110: public static final String CANCEL_KEY = Constants.CANCEL_KEY;
111:
112: /**
113: * @see Constants.VALID_KEY
114: */
115: public static final String VALID_KEY = Constants.VALID_KEY;
116:
117: /**
118: * Provide the default context attribute under which to store the
119: * transaction token key.
120: */
121: public static final String TRANSACTION_TOKEN_KEY = "TRANSACTION_TOKEN_KEY";
122:
123: /**
124: * Provide the default context attribute under which to store the token
125: * key.
126: */
127: public static final String TOKEN_KEY = "TOKEN_KEY";
128:
129: /**
130: * Store the TokenProcessor instance for this Context.
131: */
132: protected TokenProcessor token = null;
133:
134: /**
135: * Store the Log instance for this Context.
136: */
137: private Log logger = null;
138:
139: /**
140: * Instantiate ActionContextBase, wrapping the given Context.
141: *
142: * @param context Context to wrap
143: */
144: public ActionContextBase(Context context) {
145: super (context);
146: token = TokenProcessor.getInstance();
147: logger = LogFactory.getLog(this .getClass());
148: }
149:
150: /**
151: * Instantiate ActionContextBase, wrapping a default ContextBase
152: * instance.
153: */
154: public ActionContextBase() {
155: this (new ContextBase());
156: }
157:
158: // -------------------------------
159: // General Application Support
160: // -------------------------------
161: public void release() {
162: this .token = null;
163: }
164:
165: public abstract Map getApplicationScope();
166:
167: public abstract Map getRequestScope();
168:
169: public abstract Map getSessionScope();
170:
171: public Map getScope(String scopeName) {
172: if (REQUEST_SCOPE.equals(scopeName)) {
173: return this .getRequestScope();
174: }
175:
176: if (SESSION_SCOPE.equals(scopeName)) {
177: return this .getSessionScope();
178: }
179:
180: if (APPLICATION_SCOPE.equals(scopeName)) {
181: return this .getApplicationScope();
182: }
183:
184: throw new IllegalArgumentException("Invalid scope: "
185: + scopeName);
186: }
187:
188: // -------------------------------
189: // General Struts properties
190: // -------------------------------
191: public void setAction(Action action) {
192: this .put(ACTION_KEY, action);
193: }
194:
195: public Action getAction() {
196: return (Action) this .get(ACTION_KEY);
197: }
198:
199: public void setActionForm(ActionForm form) {
200: this .put(ACTION_FORM_KEY, form);
201: }
202:
203: public ActionForm getActionForm() {
204: return (ActionForm) this .get(ACTION_FORM_KEY);
205: }
206:
207: public void setActionConfig(ActionConfig config) {
208: this .put(ACTION_CONFIG_KEY, config);
209: }
210:
211: public ActionConfig getActionConfig() {
212: return (ActionConfig) this .get(ACTION_CONFIG_KEY);
213: }
214:
215: public void setForwardConfig(ForwardConfig forward) {
216: this .put(FORWARD_CONFIG_KEY, forward);
217: }
218:
219: public ForwardConfig getForwardConfig() {
220: return (ForwardConfig) this .get(FORWARD_CONFIG_KEY);
221: }
222:
223: public void setInclude(String include) {
224: this .put(INCLUDE_KEY, include);
225: }
226:
227: public String getInclude() {
228: return (String) this .get(INCLUDE_KEY);
229: }
230:
231: public Boolean getFormValid() {
232: return (Boolean) this .get(VALID_KEY);
233: }
234:
235: public void setFormValid(Boolean valid) {
236: this .put(VALID_KEY, valid);
237: }
238:
239: public ModuleConfig getModuleConfig() {
240: return (ModuleConfig) this .get(MODULE_CONFIG_KEY);
241: }
242:
243: public void setModuleConfig(ModuleConfig config) {
244: this .put(MODULE_CONFIG_KEY, config);
245: }
246:
247: public Exception getException() {
248: return (Exception) this .get(EXCEPTION_KEY);
249: }
250:
251: public void setException(Exception e) {
252: this .put(EXCEPTION_KEY, e);
253: }
254:
255: // -------------------------------
256: // ActionMessage Processing
257: // -------------------------------
258: public void addMessages(ActionMessages messages) {
259: this .addActionMessages(MESSAGE_ACTION_MESSAGES_KEY, messages);
260: }
261:
262: public void addErrors(ActionMessages errors) {
263: this .addActionMessages(ERROR_ACTION_MESSAGES_KEY, errors);
264: }
265:
266: public ActionMessages getErrors() {
267: return (ActionMessages) this .get(ERROR_ACTION_MESSAGES_KEY);
268: }
269:
270: public ActionMessages getMessages() {
271: return (ActionMessages) this .get(MESSAGE_ACTION_MESSAGES_KEY);
272: }
273:
274: public void saveErrors(ActionMessages errors) {
275: this .saveActionMessages(ERROR_ACTION_MESSAGES_KEY, errors);
276: }
277:
278: public void saveMessages(ActionMessages messages) {
279: this .saveActionMessages(MESSAGE_ACTION_MESSAGES_KEY, messages);
280: }
281:
282: // ISSUE: do we want to add this to the public API?
283:
284: /**
285: * <p> Add the given messages to a cache stored in this Context, under
286: * key. </p>
287: *
288: * @param key The attribute name for the message cache
289: * @param messages The ActionMessages to add
290: */
291: public void addActionMessages(String key, ActionMessages messages) {
292: if (messages == null) {
293: // bad programmer! *slap*
294: return;
295: }
296:
297: // get any existing messages from the request, or make a new one
298: ActionMessages requestMessages = (ActionMessages) this .get(key);
299:
300: if (requestMessages == null) {
301: requestMessages = new ActionMessages();
302: }
303:
304: // add incoming messages
305: requestMessages.add(messages);
306:
307: // if still empty, just wipe it out from the request
308: this .remove(key);
309:
310: // save the messages
311: this .saveActionMessages(key, requestMessages);
312: }
313:
314: // ISSUE: do we want to add this to the public API?
315:
316: /**
317: * <p> Save the given ActionMessages into the request scope under the
318: * given key, clearing the attribute if the messages are empty or null.
319: * </p>
320: *
321: * @param key The attribute name for the message cache
322: * @param messages The ActionMessages to add
323: */
324: public void saveActionMessages(String key, ActionMessages messages) {
325: this .saveActionMessages(REQUEST_SCOPE, key, messages);
326: }
327:
328: /**
329: * <p>Save the given <code>messages</code> into the map identified by the
330: * given <code>scopeId</code> under the given <code>key</code>.</p>
331: *
332: * @param scopeId
333: * @param key
334: * @param messages
335: */
336: public void saveActionMessages(String scopeId, String key,
337: ActionMessages messages) {
338: Map scope = getScope(scopeId);
339:
340: if ((messages == null) || messages.isEmpty()) {
341: scope.remove(key);
342:
343: return;
344: }
345:
346: scope.put(key, messages);
347: }
348:
349: // ISSUE: Should we deprecate this method, since it is misleading?
350: // Do we need it for backward compatibility?
351:
352: /**
353: * <p> Adapt a legacy form of SaveMessages to the ActionContext API by
354: * storing the ActoinMessages under the default scope.
355: *
356: * @param scope The scope for the internal cache
357: * @param messages ActionMesssages to cache
358: */
359: public void saveMessages(String scope, ActionMessages messages) {
360: this .saveMessages(messages);
361: }
362:
363: // -------------------------------
364: // Token Processing
365: // -------------------------------
366: // ISSUE: Should there be a getToken method?
367: // Is there a problem trying to map this method from Action
368: // to ActionContext when we aren't necessarily sure how token
369: // processing maps into a context with an ill-defined "session"?
370: // There's no getToken() method, but maybe there should be. *
371: public void saveToken() {
372: String token = this .generateToken();
373:
374: this .put(TRANSACTION_TOKEN_KEY, token);
375: }
376:
377: public String generateToken() {
378: return token.generateToken(getTokenGeneratorId());
379: }
380:
381: // ISSUE: The original implementation was based on the HttpSession
382: // identifier; what would be a way to do that without depending on the
383: // Servlet API?
384: // REPLY: uuid's
385: // http://java.sun.com/products/jini/2.0/doc/specs/api/net/jini/id/Uuid.html
386: protected String getTokenGeneratorId() {
387: return "";
388: }
389:
390: public boolean isTokenValid() {
391: return this .isTokenValid(false);
392: }
393:
394: public boolean isTokenValid(boolean reset) {
395: // Retrieve the transaction token from this session, and
396: // reset it if requested
397: String saved = (String) this .get(TRANSACTION_TOKEN_KEY);
398:
399: if (saved == null) {
400: return false;
401: }
402:
403: if (reset) {
404: this .resetToken();
405: }
406:
407: // Retrieve the transaction token included in this request
408: String token = (String) this .get(TOKEN_KEY);
409:
410: if (token == null) {
411: return false;
412: }
413:
414: return saved.equals(token);
415: }
416:
417: public void resetToken() {
418: this .remove(TRANSACTION_TOKEN_KEY);
419: }
420:
421: // -------------------------------
422: // Cancel Processing
423: // -------------------------------
424: public Boolean getCancelled() {
425: return (Boolean) this .get(CANCEL_KEY);
426: }
427:
428: public void setCancelled(Boolean cancelled) {
429: this .put(CANCEL_KEY, cancelled);
430: }
431:
432: // -------------------------------
433: // MessageResources Processing
434: // -------------------------------
435: public void setMessageResources(MessageResources messageResources) {
436: this .put(MESSAGE_RESOURCES_KEY, messageResources);
437: }
438:
439: public MessageResources getMessageResources() {
440: return (MessageResources) this .get(MESSAGE_RESOURCES_KEY);
441: }
442:
443: public MessageResources getMessageResources(String key) {
444: return (MessageResources) this .get(key);
445: }
446:
447: // -------------------------------
448: // Locale Processing
449: // -------------------------------
450: public void setLocale(Locale locale) {
451: this .put(LOCALE_KEY, locale);
452: }
453:
454: public Locale getLocale() {
455: return (Locale) this .get(LOCALE_KEY);
456: }
457:
458: // -------------------------------
459: // Convenience Methods: these are not part of the formal ActionContext API,
460: // but are likely to be commonly useful.
461: // -------------------------------
462:
463: /**
464: * <p> Provide the currently configured commons-logging <code>Log</code>
465: * instance. </p>
466: *
467: * @return Log instance for this context
468: */
469: public Log getLogger() {
470: return this .logger;
471: }
472:
473: /**
474: * <p> Set the commons-logging <code>Log</code> instance which should be
475: * used to LOG messages. This is initialized at instantiation time but may
476: * be overridden. Be advised not to set the value to null, as
477: * <code>ActionContextBase</code> uses the logger for some of its own
478: * operations. </p>
479: */
480: public void setLogger(Log logger) {
481: this .logger = logger;
482: }
483:
484: /**
485: * <p> Using this <code>ActionContext</code>'s default
486: * <code>ModuleConfig</code>, return an existing <code>ActionForm</code>
487: * in the specified scope, or create a new one and add it to the specified
488: * scope. </p>
489: *
490: * @param formName The name attribute of our ActionForm
491: * @param scopeName The scope identier (request, session)
492: * @return The ActionForm for this request
493: * @throws IllegalAccessException If object cannot be created
494: * @throws InstantiationException If object cannot be created
495: * @see this.findOrCreateActionForm(String, String, ModuleConfig)
496: */
497: public ActionForm findOrCreateActionForm(String formName,
498: String scopeName) throws IllegalAccessException,
499: InstantiationException {
500: return this .findOrCreateActionForm(formName, scopeName, this
501: .getModuleConfig());
502: }
503:
504: /**
505: * <p> In the context of the given <code>ModuleConfig</code> and this
506: * <code>ActionContext</code>, look for an existing
507: * <code>ActionForm</code> in the specified scope. If one is found, return
508: * it; otherwise, create a new instance, add it to that scope, and then
509: * return it. </p>
510: *
511: * @param formName The name attribute of our ActionForm
512: * @param scopeName The scope identier (request, session)
513: * @return The ActionForm for this request
514: * @throws IllegalAccessException If object cannot be created
515: * @throws InstantiationException If object cannot be created
516: * @throws IllegalArgumentException If form config is missing from module
517: * or scopeName is invalid
518: */
519: public ActionForm findOrCreateActionForm(String formName,
520: String scopeName, ModuleConfig moduleConfig)
521: throws IllegalAccessException, InstantiationException {
522: Map scope = this .getScope(scopeName);
523:
524: ActionForm instance;
525: FormBeanConfig formBeanConfig = moduleConfig
526: .findFormBeanConfig(formName);
527:
528: if (formBeanConfig == null) {
529: throw new IllegalArgumentException(
530: "No form config found under " + formName
531: + " in module " + moduleConfig.getPrefix());
532: }
533:
534: instance = (ActionForm) scope.get(formName);
535:
536: // ISSUE: Can we recycle the existing instance (if any)?
537: if (instance != null) {
538: getLogger().trace(
539: "Found an instance in scope " + scopeName
540: + "; test for reusability");
541:
542: if (formBeanConfig.canReuse(instance)) {
543: return instance;
544: }
545: }
546:
547: ActionForm form = formBeanConfig.createActionForm(this );
548:
549: // ISSUE: Should we check this call to put?
550: scope.put(formName, form);
551:
552: return form;
553: }
554: }
|