001: /*
002: * Created on Nov 23, 2004
003: */
004: package uk.org.ponder.rsf.processor;
005:
006: import java.util.Map;
007:
008: import uk.org.ponder.messageutil.TargettedMessage;
009: import uk.org.ponder.messageutil.TargettedMessageList;
010: import uk.org.ponder.rsf.flow.ARIResolver;
011: import uk.org.ponder.rsf.flow.ARIResult;
012: import uk.org.ponder.rsf.flow.ActionResultInterceptor;
013: import uk.org.ponder.rsf.flow.ActionResultInterpreter;
014: import uk.org.ponder.rsf.flow.errors.ActionErrorStrategy;
015: import uk.org.ponder.rsf.flow.support.FlowStateManager;
016: import uk.org.ponder.rsf.preservation.StatePreservationManager;
017: import uk.org.ponder.rsf.request.RequestSubmittedValueCache;
018: import uk.org.ponder.rsf.state.ErrorStateManager;
019: import uk.org.ponder.rsf.state.RSVCApplier;
020: import uk.org.ponder.rsf.viewstate.AnyViewParameters;
021: import uk.org.ponder.rsf.viewstate.ViewParameters;
022: import uk.org.ponder.util.Logger;
023: import uk.org.ponder.util.RunnableInvoker;
024: import uk.org.ponder.util.UniversalRuntimeException;
025:
026: /**
027: * ActionHandler is a request scope bean responsible for handling an HTTP POST
028: * request, or other non-idempotent web service "action" cycle. Defines the core
029: * logic for this processing cycle.
030: *
031: * @author Antranig Basman (antranig@caret.cam.ac.uk)
032: */
033: public class RSFActionHandler implements ActionHandler, ErrorHandler {
034: // application-scope dependencies
035: private ARIResolver ariresolver;
036: private RunnableInvoker postwrapper;
037: private RequestSubmittedValueCache requestrsvc;
038:
039: // request-scope dependencies
040: private Map normalizedmap;
041: private ViewParameters viewparams;
042: private ErrorStateManager errorstatemanager;
043: private RSVCApplier rsvcapplier;
044: private StatePreservationManager presmanager; // no, not that of OS/2
045: private ActionErrorStrategy actionerrorstrategy;
046: private FlowStateManager flowstatemanager;
047: private TargettedMessageList messages;
048: private ActionResultInterceptor arinterceptor;
049:
050: public void setFlowStateManager(FlowStateManager flowstatemanager) {
051: this .flowstatemanager = flowstatemanager;
052: }
053:
054: public void setErrorStateManager(ErrorStateManager errorstatemanager) {
055: this .errorstatemanager = errorstatemanager;
056: }
057:
058: public void setARIResolver(ARIResolver ariresolver) {
059: this .ariresolver = ariresolver;
060: }
061:
062: public void setActionResultInterceptor(
063: ActionResultInterceptor arinterceptor) {
064: this .arinterceptor = arinterceptor;
065: }
066:
067: public void setViewParameters(ViewParameters viewparams) {
068: this .viewparams = viewparams;
069: }
070:
071: public void setRequestRSVC(RequestSubmittedValueCache requestrsvc) {
072: this .requestrsvc = requestrsvc;
073: }
074:
075: public void setAlterationWrapper(RunnableInvoker postwrapper) {
076: this .postwrapper = postwrapper;
077: }
078:
079: public void setRSVCApplier(RSVCApplier rsvcapplier) {
080: this .rsvcapplier = rsvcapplier;
081: }
082:
083: public void setNormalizedRequestMap(Map normalizedmap) {
084: this .normalizedmap = normalizedmap;
085: }
086:
087: public void setStatePreservationManager(
088: StatePreservationManager presmanager) {
089: this .presmanager = presmanager;
090: }
091:
092: // actually the ActionErrorStrategyManager
093: public void setActionErrorStrategy(
094: ActionErrorStrategy actionerrorstrategy) {
095: this .actionerrorstrategy = actionerrorstrategy;
096: }
097:
098: public void setTargettedMessageList(TargettedMessageList messages) {
099: this .messages = messages;
100: }
101:
102: // The public ARIResult after all inference is concluded.
103: private ARIResult ariresult = null;
104: // The original raw action result - cannot make this final for wrapper access
105: private Object actionresult = null;
106:
107: /**
108: * The result of this post cycle will be of interest to some other request
109: * beans, in particular the alteration wrapper. This bean must however be
110: * propagated lazily since it is only constructed partway through this
111: * handler, if indeed it is a POST cycle at all.
112: *
113: * @return The ARIResult resulting from this action cycle.
114: */
115: public ARIResult getARIResult() {
116: return ariresult;
117: }
118:
119: public Object handleError(Object actionresult, Exception exception) {
120: // an ARIResult is at the end-of-line - this is now unsupported though.
121: if (actionresult != null && !(actionresult instanceof String))
122: return actionresult;
123: TargettedMessage tmessage = new TargettedMessage();
124: Object newcode = actionerrorstrategy.handleError(
125: (String) actionresult, exception, null,
126: viewparams.viewID, tmessage);
127:
128: if (newcode != null && !(newcode instanceof String))
129: return newcode;
130: for (int i = 0; i < messages.size(); ++i) {
131: TargettedMessage message = messages.messageAt(i);
132: if (message.exception != null) {
133: try {
134: Object testcode = actionerrorstrategy.handleError(
135: (String) newcode, message.exception, null,
136: viewparams.viewID, message);
137: if (testcode != null)
138: newcode = testcode;
139: } catch (Exception e) {
140: // rethrow *original* more accurate URE, as per Az discovery
141: throw UniversalRuntimeException.accumulate(
142: message.exception, "Error invoking action");
143: }
144:
145: }
146: }
147: if (tmessage.messagecodes != null) {
148: messages.addMessage(tmessage);
149: }
150: return newcode;
151: }
152:
153: public AnyViewParameters handle() {
154:
155: try {
156: final String actionmethod = PostDecoder
157: .decodeAction(normalizedmap);
158: // Do this FIRST in case it discovers any scopelocks required
159: presmanager.scopeRestore();
160: // invoke all state-altering operations within the runnable wrapper.
161: postwrapper.invokeRunnable(new Runnable() {
162: public void run() {
163:
164: if (viewparams.flowtoken != null) {
165: presmanager.restore(viewparams.flowtoken,
166: viewparams.endflow != null);
167: }
168: Exception exception = null;
169: try {
170: rsvcapplier.applyValues(requestrsvc); // many errors possible here.
171: } catch (Exception e) {
172: exception = e;
173: }
174: Object newcode = handleError(actionresult,
175: exception);
176: exception = null;
177: if ((newcode == null && !messages.isError())
178: || newcode instanceof String) {
179: // only proceed to actually invoke action if no ARIResult already
180: // note all this odd two-step procedure is only required to be able
181: // to pass AES error returns and make them "appear" to be the
182: // returns of the first action method in a Flow.
183: try {
184: if (actionmethod != null) {
185: actionresult = rsvcapplier
186: .invokeAction(actionmethod,
187: (String) newcode);
188: }
189: } catch (Exception e) {
190: exception = e;
191: }
192: newcode = handleError(actionresult, exception);
193: }
194: if (newcode != null)
195: actionresult = newcode;
196: // must interpret ARI INSIDE the wrapper, since it may need it
197: // on closure.
198: if (actionresult instanceof ARIResult) {
199: ariresult = (ARIResult) actionresult;
200: } else {
201: ActionResultInterpreter ari = ariresolver
202: .getActionResultInterpreter();
203: ariresult = ari.interpretActionResult(
204: viewparams, actionresult);
205: }
206: arinterceptor.interceptActionResult(ariresult,
207: viewparams, actionresult);
208: }
209: });
210: presmanager.scopePreserve();
211:
212: flowstatemanager.inferFlowState(viewparams, ariresult);
213:
214: // moved inside since this may itself cause an error!
215: String submitting = PostDecoder
216: .decodeSubmittingControl(normalizedmap);
217: errorstatemanager.globaltargetid = submitting;
218: } catch (Throwable e) { // avoid masking errors from the finally block
219: Logger.log.warn("Error invoking action", e);
220: // ThreadErrorState.addError(new TargettedMessage(
221: // CoreMessages.GENERAL_ACTION_ERROR));
222: // Detect failure to fill out arires properly.
223: if (ariresult == null || ariresult.resultingView == null
224: || e instanceof IllegalStateException) {
225: ariresult = new ARIResult();
226: ariresult.propagateBeans = ARIResult.FLOW_END;
227:
228: ViewParameters defaultparameters = viewparams
229: .copyBase();
230: ariresult.resultingView = defaultparameters;
231: }
232: } finally {
233: String errortoken = null;
234: if (messages.size() != 0) {
235: // messages on a POST cycle are DEFINITELY from our cycle
236: errortoken = errorstatemanager.allocateOutgoingToken();
237: }
238: errorstatemanager.requestComplete();
239:
240: if (ariresult.resultingView instanceof ViewParameters) {
241: ((ViewParameters) ariresult.resultingView).errortoken = errortoken;
242: }
243: }
244: return ariresult.resultingView;
245: }
246:
247: }
|