001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.webflow.executor.jsf;
017:
018: import javax.faces.component.UIComponent;
019: import javax.faces.component.UIComponentBase;
020: import javax.faces.component.UIViewRoot;
021: import javax.faces.context.FacesContext;
022: import javax.faces.event.PhaseId;
023: import javax.faces.render.Renderer;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.springframework.util.StringUtils;
028: import org.springframework.webflow.execution.FlowExecution;
029: import org.springframework.webflow.execution.repository.FlowExecutionAccessException;
030: import org.springframework.webflow.execution.repository.FlowExecutionKey;
031: import org.springframework.webflow.execution.repository.FlowExecutionLock;
032: import org.springframework.webflow.execution.repository.FlowExecutionRepository;
033:
034: /**
035: * This {@link UIComponent} instance can be added to the {@link UIViewRoot} before rendering so that the
036: * {@link FlowExecution} can be properly saved and then restored during the next request's {@link PhaseId#RESTORE_VIEW}
037: * phase.
038: *
039: * @author Jeremy Grelle
040: * @author Keith Donald
041: */
042: public class FlowExecutionKeyStateHolder extends UIComponentBase {
043:
044: /**
045: * Logger, usable by subclasses.
046: */
047: protected final Log logger = LogFactory.getLog(getClass());
048:
049: private static final String COMPONENT_FAMILY = "javax.faces.Parameter";
050:
051: /**
052: * Immutable id of the flow execution key component for easier lookup later.
053: */
054: public static final String COMPONENT_ID = "FlowExecutionKeyStateHolder";
055:
056: /**
057: * The key value
058: */
059: private String flowExecutionKey;
060:
061: private boolean transientValue;
062:
063: public String getId() {
064: return COMPONENT_ID;
065: }
066:
067: public void setId(String id) {
068: // Do nothing so as to ensure the id never gets overwritten.
069: return;
070: }
071:
072: public String getFamily() {
073: return COMPONENT_FAMILY;
074: }
075:
076: public Renderer getRenderer() {
077: // this component is not rendered
078: return null;
079: }
080:
081: /**
082: * Returns the flow execution key.
083: */
084: public String getFlowExecutionKey() {
085: return flowExecutionKey;
086: }
087:
088: /**
089: * Sets the tracked flow execution key used to restore the current flow execution during
090: * {@link #restoreState(FacesContext, Object)}.
091: * @param flowExecutionKey the flow execution key
092: */
093: public void setFlowExecutionKey(String flowExecutionKey) {
094: this .flowExecutionKey = flowExecutionKey;
095: }
096:
097: public boolean isTransient() {
098: return transientValue;
099: }
100:
101: public void setTransient(boolean transientValue) {
102: this .transientValue = transientValue;
103: }
104:
105: /**
106: * Restore the FlowExecution from the stored FlowExecutionKey
107: */
108: public void restoreState(FacesContext context, Object state) {
109: Object values[] = (Object[]) state;
110: flowExecutionKey = (String) values[0];
111: restoreFlowExecution(context);
112: }
113:
114: private void restoreFlowExecution(FacesContext facesContext) {
115: JsfExternalContext context = new JsfExternalContext(
116: facesContext);
117: // restore only if the key is present and the current flow execution has not already been restored
118: if (StringUtils.hasText(flowExecutionKey)
119: && !FlowExecutionHolderUtils
120: .isFlowExecutionRestored(facesContext)) {
121: // restore the "current" flow execution from repository so it will be available to variable/property resolvers
122: // and the flow navigation handler (this could happen as part of a view action like a form submission)
123: FlowExecutionRepository repository = getRepository(context);
124: // restore the key from the stored encoded key string
125: FlowExecutionKey key = repository
126: .parseFlowExecutionKey(flowExecutionKey);
127: try {
128: FlowExecutionLock lock = repository.getLock(key);
129: lock.lock();
130: try {
131: FlowExecution flowExecution = repository
132: .getFlowExecution(key);
133: if (logger.isDebugEnabled()) {
134: logger
135: .debug("Loaded existing flow execution with key '"
136: + flowExecutionKey
137: + "' as part of component restoration [triggered via an action event like a button click]");
138: }
139: FlowExecutionHolderUtils.setFlowExecutionHolder(
140: new FlowExecutionHolder(key, flowExecution,
141: lock), facesContext);
142: } catch (RuntimeException e) {
143: lock.unlock();
144: throw e;
145: } catch (Error e) {
146: lock.unlock();
147: throw e;
148: }
149: } catch (FlowExecutionAccessException e) {
150: handleFlowExecutionAccessException(e, facesContext);
151: }
152: }
153: }
154:
155: /**
156: * Hook method to handle a thrown flow execution access exception. By default this implementation simply rethrows
157: * the exception. Subclasses may override this method to redirect to an error page or take some other action.
158: * @param e the flow execution access exception
159: * @param context the current faces context
160: */
161: protected void handleFlowExecutionAccessException(
162: FlowExecutionAccessException e, FacesContext context) {
163: throw e;
164: }
165:
166: /**
167: * Save the just the current FlowExecutionKey value.
168: */
169: public Object saveState(FacesContext context) {
170: Object values[] = new Object[1];
171: values[0] = flowExecutionKey;
172: return values;
173: }
174:
175: public String getClientId(FacesContext context) {
176: return COMPONENT_ID;
177: }
178:
179: private FlowExecutionRepository getRepository(
180: JsfExternalContext context) {
181: return FlowFacesUtils.getExecutionRepository(context
182: .getFacesContext());
183: }
184: }
|