001: /*
002: * Created on 15 Jul 2007
003: */
004:
005: package uk.org.ponder.rsf.swf.support;
006:
007: import java.util.HashMap;
008: import java.util.Iterator;
009: import java.util.Map;
010:
011: import org.springframework.beans.factory.DisposableBean;
012: import org.springframework.webflow.context.ExternalContext;
013: import org.springframework.webflow.conversation.impl.SessionBindingConversationManager;
014: import org.springframework.webflow.core.collection.LocalParameterMap;
015: import org.springframework.webflow.core.collection.LocalSharedAttributeMap;
016: import org.springframework.webflow.core.collection.MutableAttributeMap;
017: import org.springframework.webflow.core.collection.ParameterMap;
018: import org.springframework.webflow.core.collection.SharedAttributeMap;
019:
020: import uk.org.ponder.rsf.state.LockGetter;
021: import uk.org.ponder.rsf.state.TSHMap;
022: import uk.org.ponder.rsf.state.TokenStateHolder;
023: import uk.org.ponder.rsf.swf.util.ConcreteSharedMap;
024:
025: /**
026: * Implementation of the "External Context" abstraction for the RSF environment.
027: * Implementation notes: General search through the SWF codebase has determined
028: * that with a few exceptions, the majority of ExternalContext methods are used
029: * only within the execution.support package, which appears to be for aiding
030: * integration work. RSF supplies its own environmental portability so in
031: * general we will not supply implementations for most of these methods for now.
032: * Recommendation for non-portable RSF-SWF apps is that they make use of the
033: * local RSF features for non-portable code (direct injection of request-scope environment beans).
034: * <br/>
035: * The methods we *must* and do implement are: {@link #getRequestParameterMap()} -
036: * called in numerous places {@link #getSessionMap()} - called from
037: * {@link SessionBindingConversationManager} getConversationContainer
038: * (only <code>put()</code> support required)
039: *
040: * @author Antranig Basman (antranig@caret.cam.ac.uk)
041: *
042: */
043:
044: public class RSFSWFExternalContext implements ExternalContext,
045: DisposableBean {
046:
047: private TokenStateHolder tokenStateHolder;
048:
049: private LockGetter lockGetter;
050:
051: private Map parameterMap;
052:
053: private ParameterMapInterpreter parameterMapInterpreter;
054:
055: public void setLockGetter(LockGetter lockGetter) {
056: this .lockGetter = lockGetter;
057: }
058:
059: public void setTokenStateHolder(TokenStateHolder tokenStateHolder) {
060: this .tokenStateHolder = tokenStateHolder;
061: }
062:
063: // Currently inject the interpreter directly - since we have TWO executions
064: // of SWF per action cycle, one for VPI and one after submission - we actually
065: // return a different request map on each occasion.
066: public void setParameterMapInterpreter(
067: ParameterMapInterpreter parameterMapInterpreter) {
068: this .parameterMapInterpreter = parameterMapInterpreter;
069: }
070:
071: public SharedAttributeMap getSessionMap() {
072: return new LocalSharedAttributeMap(new ConcreteSharedMap(
073: new TSHMap(tokenStateHolder), lockGetter
074: .getLock(tokenStateHolder.getId())));
075: }
076:
077: public void destroy() throws Exception {
078: // This destroy() is NOT as safe as a finally(), but it is the best we
079: // can do with this strategy. Should look at a MUTEX-like solution as per
080: // Spring Web. The BasicScopedAlterationWrapper will in general always
081: // deallocate the lock first anyway.
082: lockGetter.returnLock(tokenStateHolder.getId());
083: }
084:
085: public void setParameterMap(Map parameterMap) {
086: this .parameterMap = cleanseParameterMap(parameterMap);
087: }
088:
089: public ParameterMap getRequestParameterMap() {
090: return new LocalParameterMap(
091: cleanseParameterMap(parameterMapInterpreter
092: .getParameterMap()));
093: }
094:
095: private Map cleanseParameterMap(Map params) {
096: // SWF contract is that String array parameters are folded
097: Map togo = new HashMap();
098: for (Iterator keyit = params.keySet().iterator(); keyit
099: .hasNext();) {
100: String key = (String) keyit.next();
101: Object value = params.get(key);
102: if (value instanceof String) {
103: togo.put(key, value);
104: } else if (value instanceof String[]) {
105: togo.put(key, ((String[]) value)[0]);
106: }
107: }
108: return togo;
109: }
110:
111: public String getContextPath() {
112: throw unsupported();
113: }
114:
115: public String getDispatcherPath() {
116: throw unsupported();
117: }
118:
119: public SharedAttributeMap getGlobalSessionMap() {
120: throw unsupported();
121: }
122:
123: public MutableAttributeMap getRequestMap() {
124: throw unsupported();
125: }
126:
127: public SharedAttributeMap getApplicationMap() {
128: throw unsupported();
129: }
130:
131: public String getRequestPathInfo() {
132: throw unsupported();
133: }
134:
135: private RuntimeException unsupported() {
136: return new UnsupportedOperationException(
137: "Not supported in RSF environment");
138: }
139:
140: }
|