diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java b/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java index e60f7e13..4fdba354 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java @@ -172,4 +172,10 @@ public interface ExternalContext { */ public boolean isResponseCommitted(); + /** + * Is a response allowed for this request + * @return true if yes, false otherwise + */ + public boolean isResponseAllowed(); + } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandler.java new file mode 100644 index 00000000..ba2f7055 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandler.java @@ -0,0 +1,43 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.springframework.webflow.context.portlet; + +import javax.portlet.ActionResponse; +import javax.portlet.PortletRequest; +import javax.portlet.PortletURL; +import javax.portlet.RenderResponse; + +/** + * Default flow URL handler for SWF 2. + * + * @author Scott Andrews + */ +public class DefaultFlowUrlHandler implements FlowUrlHandler { + + public String getFlowExecutionKey(PortletRequest request) { + return request.getParameter("execution"); + } + + public void setFlowExecutionRenderParameter(String flowExecutionKey, ActionResponse response) { + response.setRenderParameter("execution", flowExecutionKey); + } + + public String createFlowExecutionUrl(String flowId, String flowExecutionKey, RenderResponse response) { + PortletURL url = response.createActionURL(); + url.setParameter("execution", flowExecutionKey); + return url.toString(); + } +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/FlowUrlHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/FlowUrlHandler.java new file mode 100644 index 00000000..273dd1a8 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/FlowUrlHandler.java @@ -0,0 +1,46 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.springframework.webflow.context.portlet; + +import javax.portlet.ActionResponse; +import javax.portlet.PortletRequest; +import javax.portlet.RenderResponse; + +/** + * A interface for parsing and generating flow URLs. Encapsulates a specific flow URL format. + * + * @author Keith Donald + * @author Scott Andrews + */ +public interface FlowUrlHandler { + + /** + * Extract the flow execution from the request. + * @param request the request + * @return the flow execution key, or null if no flow execution key is present + */ + public String getFlowExecutionKey(PortletRequest request); + + /** + * Set the flow execution key render parameter. + * @param flowExecutionKey the key + * @param response the action response + */ + public void setFlowExecutionRenderParameter(String flowExecutionKey, ActionResponse response); + + public String createFlowExecutionUrl(String flowId, String flowExecutionKey, RenderResponse response); + +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletContextMap.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletContextMap.java new file mode 100644 index 00000000..c2c4414f --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletContextMap.java @@ -0,0 +1,65 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.springframework.webflow.context.portlet; + +import java.util.Iterator; + +import javax.portlet.PortletContext; + +import org.springframework.binding.collection.SharedMap; +import org.springframework.binding.collection.StringKeyedMapAdapter; +import org.springframework.webflow.core.collection.CollectionUtils; + +/** + * Map backed by the Portlet context for accessing application scoped attributes. + * + * @author Keith Donald + * @author Scott Andrews + */ +public class PortletContextMap extends StringKeyedMapAdapter implements SharedMap { + + /** + * The wrapped portlet context. + */ + private PortletContext context; + + /** + * Create a map wrapping given portlet context. + */ + public PortletContextMap(PortletContext context) { + this.context = context; + } + + protected Object getAttribute(String key) { + return context.getAttribute(key); + } + + protected void setAttribute(String key, Object value) { + context.setAttribute(key, value); + } + + protected void removeAttribute(String key) { + context.removeAttribute(key); + } + + protected Iterator getAttributeNames() { + return CollectionUtils.toIterator(context.getAttributeNames()); + } + + public Object getMutex() { + return context; + } +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletExternalContext.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletExternalContext.java new file mode 100644 index 00000000..f2915ba3 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletExternalContext.java @@ -0,0 +1,325 @@ +package org.springframework.webflow.context.portlet; + +import java.io.IOException; +import java.io.Writer; +import java.security.Principal; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +import javax.portlet.PortletContext; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import org.springframework.webflow.context.ExternalContext; +import org.springframework.webflow.core.collection.AttributeMap; +import org.springframework.webflow.core.collection.LocalAttributeMap; +import org.springframework.webflow.core.collection.LocalParameterMap; +import org.springframework.webflow.core.collection.LocalSharedAttributeMap; +import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.core.collection.ParameterMap; +import org.springframework.webflow.core.collection.SharedAttributeMap; + +/** + * Provides contextual information about an portlet environment that has interacted with Spring Web Flow. + * + * @author Keith Donald + * @author Erwin Vervaet + * @author Jeremy Grelle + * @author Scott Andrews + */ +public class PortletExternalContext implements ExternalContext { + + protected static final short ACTION_PHASE = 1; + protected static final short RENDER_PHASE = 2; + + /** + * The context. + */ + private PortletContext context; + + /** + * The request. + */ + private PortletRequest request; + + /** + * The response. + */ + private PortletResponse response; + + /** + * The portlet request phase: render or action + */ + private short requestPhase; + + /** + * An accessor for the HTTP request parameter map. + */ + private ParameterMap requestParameterMap; + + /** + * An accessor for the HTTP request attribute map. + */ + private MutableAttributeMap requestMap; + + /** + * An accessor for the HTTP session map. + */ + private SharedAttributeMap sessionMap; + + /** + * An accessor for the servlet context application map. + */ + private SharedAttributeMap applicationMap; + + /** + * A flag indicating if a flow execution redirect has been requested. + */ + private boolean flowExecutionRedirectRequested; + + /** + * A string specifying the id of the flow to redirect to after request processing. If null, no flow definition + * redirect has been requested. + */ + private String flowDefinitionRedirectFlowId; + + /** + * Input to pass the flow definition upon redirecting. May be null. Never set unless + * {@link #flowDefinitionRedirectFlowId} has been set. + */ + private AttributeMap flowDefinitionRedirectFlowInput; + + /** + * A string specifying an arbitrary + */ + private String externalRedirectUrl; + + /** + * The strategy for generating flow execution urls. + */ + private FlowUrlHandler flowUrlHandler; + + /** + * Whether this external request context originated from an Ajax request or not. + */ + private boolean ajaxRequest; + + /** + * In the case where a redirect response is requested, this flag indicates if the redirect should be issued from a + * popup dialog. + */ + private boolean redirectInPopup; + + /** + * Create a new external context wrapping given portlet action request and response and given portlet context. + * @param context the portal context + * @param request the portlet request + * @param response the portlet response + */ + public PortletExternalContext(PortletContext context, PortletRequest request, PortletResponse response) { + init(context, request, response, new DefaultFlowUrlHandler()); + } + + /** + * Create a new external context wrapping given portlet action request and response and given portlet context. + * @param context the portal context + * @param request the portlet request + * @param response the portlet response + * @param flowUrlHandler the flow url handler + */ + public PortletExternalContext(PortletContext context, PortletRequest request, PortletResponse response, + FlowUrlHandler flowUrlHandler) { + init(context, request, response, flowUrlHandler); + } + + /** + * Indicates if the current request from this client is an ajax request. This flag may effect the handling of + * response writing within Spring Web Flow. + * @param ajaxRequest the ajax request flag + */ + public void setAjaxRequest(boolean ajaxRequest) { + this.ajaxRequest = ajaxRequest; + } + + // implementing external context + + public String getContextPath() { + return request.getContextPath(); + } + + public ParameterMap getRequestParameterMap() { + return requestParameterMap; + } + + public MutableAttributeMap getRequestMap() { + return requestMap; + } + + public SharedAttributeMap getSessionMap() { + return sessionMap; + } + + public SharedAttributeMap getGlobalSessionMap() { + return getSessionMap(); + } + + public SharedAttributeMap getApplicationMap() { + return applicationMap; + } + + public Principal getCurrentUser() { + return request.getUserPrincipal(); + } + + public Object getNativeContext() { + return context; + } + + public Object getNativeRequest() { + return request; + } + + public Object getNativeResponse() { + return response; + } + + public boolean isAjaxRequest() { + return ajaxRequest; + } + + public String getFlowExecutionUri(String flowId, String flowExecutionKey) { + if (this.isRenderPhase()) { + return flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, (RenderResponse) response); + } else { + throw new IllegalStateException("Only a render request can obtain an flow execution uri"); + } + } + + public Writer getResponseWriter() { + try { + if (isRenderPhase()) { + return ((RenderResponse) response).getWriter(); + } else { + throw new IllegalStateException("Only render requests can obtain response writer"); + } + } catch (IOException e) { + throw new IllegalStateException("Unable to obtain response writer", e); + } + } + + public boolean isResponseCommitted() { + return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested(); + } + + public boolean isResponseAllowed() { + return isRenderPhase(); + } + + public void requestFlowExecutionRedirect() { + flowExecutionRedirectRequested = true; + } + + public void requestExternalRedirect(String uri) { + externalRedirectUrl = uri; + } + + public void requestFlowDefinitionRedirect(String flowId, AttributeMap input) { + flowDefinitionRedirectFlowId = flowId; + flowDefinitionRedirectFlowInput = input; + } + + public void requestRedirectInPopup() { + redirectInPopup = true; + } + + // implementation specific methods + + /** + * Returns the flag indicating if a flow execution redirect response has been requested by the flow. + */ + public boolean flowExecutionRedirectRequested() { + return flowExecutionRedirectRequested; + } + + /** + * Returns the flag indicating if a flow definition redirect response has been requested by the flow. + */ + public boolean flowDefinitionRedirectRequested() { + return flowDefinitionRedirectFlowId != null; + } + + /** + * Returns the id of the flow definition to redirect to. Only set when {@link #flowDefinitionRedirectRequested()} + * returns true. + */ + public String getFlowRedirectFlowId() { + return flowDefinitionRedirectFlowId; + } + + /** + * Returns the input to pass the flow definition through the redirect. Only set when + * {@link #flowDefinitionRedirectRequested()} returns true. + */ + public AttributeMap getFlowRedirectFlowInput() { + return flowDefinitionRedirectFlowInput; + } + + /** + * Returns the flag indicating if an external redirect response has been requested by the flow. + */ + public boolean externalRedirectRequested() { + return externalRedirectUrl != null; + } + + /** + * Returns the URL to redirect to. Only set if {@link #externalRedirectRequested()} returns true. + */ + public String getExternalRedirectUrl() { + return externalRedirectUrl; + } + + /** + * If a redirect response has been requested, indicates if the redirect should be issued from a popup dialog. + */ + public boolean redirectInPopup() { + return redirectInPopup; + } + + /** + * Returns true if the current request phase is the action phase + */ + public boolean isActionPhase() { + return requestPhase == ACTION_PHASE; + } + + /** + * Returns true if the current request phase is the render phase + */ + public boolean isRenderPhase() { + return requestPhase == RENDER_PHASE; + } + + // private helpers + + private void init(PortletContext context, PortletRequest request, PortletResponse response, + FlowUrlHandler flowUrlHandler) { + this.context = context; + this.request = request; + this.response = response; + this.requestParameterMap = new LocalParameterMap(new PortletRequestParameterMap(request)); + this.requestMap = new LocalAttributeMap(new PortletRequestMap(request)); + this.sessionMap = new LocalSharedAttributeMap(new PortletSessionMap(request)); + this.applicationMap = new LocalSharedAttributeMap(new PortletContextMap(context)); + this.flowUrlHandler = flowUrlHandler; + if (request instanceof ActionRequest && response instanceof ActionResponse) { + requestPhase = ACTION_PHASE; + } else if (request instanceof RenderRequest && response instanceof RenderResponse) { + requestPhase = RENDER_PHASE; + } else { + throw new IllegalArgumentException("Unknown portlet phase, expected: action or render"); + } + } + +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletRequestMap.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletRequestMap.java new file mode 100644 index 00000000..4cad852d --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletRequestMap.java @@ -0,0 +1,60 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.springframework.webflow.context.portlet; + +import java.util.Iterator; + +import javax.portlet.PortletRequest; + +import org.springframework.binding.collection.StringKeyedMapAdapter; +import org.springframework.webflow.core.collection.CollectionUtils; + +/** + * Map backed by the Portlet request attribute map for accessing request local attributes. + * + * @author Keith Donald + * @author Scott Andrews + */ +public class PortletRequestMap extends StringKeyedMapAdapter { + + /** + * The wrapped portlet request. + */ + private PortletRequest request; + + /** + * Create a new map wrapping the attributes of given request. + */ + public PortletRequestMap(PortletRequest request) { + this.request = request; + } + + protected Object getAttribute(String key) { + return request.getAttribute(key); + } + + protected void setAttribute(String key, Object value) { + request.setAttribute(key, value); + } + + protected void removeAttribute(String key) { + request.removeAttribute(key); + } + + protected Iterator getAttributeNames() { + return CollectionUtils.toIterator(request.getAttributeNames()); + } +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletRequestParameterMap.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletRequestParameterMap.java new file mode 100644 index 00000000..9bdd6dc8 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletRequestParameterMap.java @@ -0,0 +1,87 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.springframework.webflow.context.portlet; + +import java.util.Iterator; + +import javax.portlet.PortletRequest; + +import org.springframework.binding.collection.CompositeIterator; +import org.springframework.binding.collection.StringKeyedMapAdapter; +import org.springframework.util.Assert; +import org.springframework.web.portlet.multipart.MultipartActionRequest; +import org.springframework.webflow.core.collection.CollectionUtils; + +/** + * Map backed by the Portlet request parameter map for accessing request parameters. Also provides support for + * multi-part requests, providing transparent access to the request "fileMap" as a request parameter entry. + * + * @author Keith Donald + * @author Scott Andrews + */ +public class PortletRequestParameterMap extends StringKeyedMapAdapter { + + /** + * The wrapped Portlet request. + */ + private PortletRequest request; + + /** + * Create a new map wrapping the parameters of given request. + */ + public PortletRequestParameterMap(PortletRequest request) { + Assert.notNull(request, "The portlet request is required"); + this.request = request; + } + + protected Object getAttribute(String key) { + if (request instanceof MultipartActionRequest) { + MultipartActionRequest multipartRequest = (MultipartActionRequest) request; + Object data = multipartRequest.getFileMap().get(key); + if (data != null) { + return data; + } + } + String[] parameters = request.getParameterValues(key); + if (parameters == null) { + return null; + } else if (parameters.length == 1) { + return parameters[0]; + } else { + return parameters; + } + } + + protected void setAttribute(String key, Object value) { + throw new UnsupportedOperationException("PortletRequest parameter maps are immutable"); + } + + protected void removeAttribute(String key) { + throw new UnsupportedOperationException("PortletRequest parameter maps are immutable"); + } + + protected Iterator getAttributeNames() { + if (request instanceof MultipartActionRequest) { + MultipartActionRequest multipartRequest = (MultipartActionRequest) request; + CompositeIterator iterator = new CompositeIterator(); + iterator.add(multipartRequest.getFileMap().keySet().iterator()); + iterator.add(CollectionUtils.toIterator(request.getParameterNames())); + return iterator; + } else { + return CollectionUtils.toIterator(request.getParameterNames()); + } + } +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletSessionMap.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletSessionMap.java new file mode 100644 index 00000000..28ae0b2b --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletSessionMap.java @@ -0,0 +1,104 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.springframework.webflow.context.portlet; + +import java.util.Iterator; + +import javax.portlet.PortletRequest; +import javax.portlet.PortletSession; + +import org.springframework.binding.collection.SharedMap; +import org.springframework.binding.collection.StringKeyedMapAdapter; +import org.springframework.web.util.WebUtils; +import org.springframework.webflow.context.web.HttpSessionMapBindingListener; +import org.springframework.webflow.core.collection.AttributeMapBindingListener; +import org.springframework.webflow.core.collection.CollectionUtils; + +/** + * A Shared Map backed by the Portlet session, for accessing session scoped attributes. + * + * @author Keith Donald + * @author Scott Andrews + */ +public class PortletSessionMap extends StringKeyedMapAdapter implements SharedMap { + + /** + * The wrapped portlet request, providing access to the session. + */ + private PortletRequest request; + + /** + * Create a map wrapping the session of given request. + */ + public PortletSessionMap(PortletRequest request) { + this.request = request; + } + + /** + * Internal helper to get the portlet session associated with the wrapped request, or null if there is no such + * session. + *
+ * Note that this method will not force session creation. + */ + private PortletSession getSession() { + return request.getPortletSession(false); + } + + protected Object getAttribute(String key) { + PortletSession session = getSession(); + if (session == null) { + return null; + } + Object value = session.getAttribute(key); + if (value instanceof HttpSessionMapBindingListener) { + // unwrap + return ((HttpSessionMapBindingListener) value).getListener(); + } else { + return value; + } + } + + protected void setAttribute(String key, Object value) { + // force session creation + PortletSession session = request.getPortletSession(true); + if (value instanceof AttributeMapBindingListener) { + // wrap + session.setAttribute(key, new HttpSessionMapBindingListener((AttributeMapBindingListener) value, this)); + } else { + session.setAttribute(key, value); + } + } + + protected void removeAttribute(String key) { + PortletSession session = getSession(); + if (session != null) { + session.removeAttribute(key); + } + } + + protected Iterator getAttributeNames() { + PortletSession session = getSession(); + return session == null ? CollectionUtils.EMPTY_ITERATOR : CollectionUtils.toIterator(session + .getAttributeNames()); + } + + public Object getMutex() { + // force session creation + PortletSession session = request.getPortletSession(true); + Object mutex = session.getAttribute(WebUtils.SESSION_MUTEX_ATTRIBUTE); + return mutex != null ? mutex : session; + } +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/HttpSessionMap.java b/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/HttpSessionMap.java index 0a61bc4f..9233906e 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/HttpSessionMap.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/HttpSessionMap.java @@ -23,6 +23,7 @@ import javax.servlet.http.HttpSession; import org.springframework.binding.collection.SharedMap; import org.springframework.binding.collection.StringKeyedMapAdapter; import org.springframework.web.util.WebUtils; +import org.springframework.webflow.context.web.HttpSessionMapBindingListener; import org.springframework.webflow.core.collection.AttributeMapBindingListener; import org.springframework.webflow.core.collection.CollectionUtils; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java b/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java index 90b53d02..d84e8ade 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java @@ -207,6 +207,10 @@ public class ServletExternalContext implements ExternalContext { return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested(); } + public boolean isResponseAllowed() { + return true; + } + public void requestFlowExecutionRedirect() { flowExecutionRedirectRequested = true; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/HttpSessionMapBindingListener.java b/spring-webflow/src/main/java/org/springframework/webflow/context/web/HttpSessionMapBindingListener.java similarity index 94% rename from spring-webflow/src/main/java/org/springframework/webflow/context/servlet/HttpSessionMapBindingListener.java rename to spring-webflow/src/main/java/org/springframework/webflow/context/web/HttpSessionMapBindingListener.java index a873915b..f7aa5587 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/HttpSessionMapBindingListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/web/HttpSessionMapBindingListener.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package org.springframework.webflow.context.servlet; +package org.springframework.webflow.context.web; import java.util.Map; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java index f8305b52..4bf22702 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java @@ -96,7 +96,7 @@ public class EndState extends State { FlowSession activeSession = context.getFlowExecutionContext().getActiveSession(); if (activeSession.isRoot()) { // entire flow execution is ending; issue the final response - if (finalResponseAction != null) { + if (finalResponseAction != null && context.getExternalContext().isResponseAllowed()) { ActionExecutor.execute(finalResponseAction, context); } context.endActiveFlowSession(createSessionOutput(context)); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java index ec474a53..c38654b4 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java @@ -161,14 +161,16 @@ public class ViewState extends TransitionableState { protected void doEnter(RequestControlContext context) throws FlowExecutionException { context.assignFlowExecutionKey(); - if (shouldRedirect(context)) { - context.getExternalContext().requestFlowExecutionRedirect(); - if (popup) { - context.getExternalContext().requestRedirectInPopup(); + if (context.getExternalContext().isResponseAllowed()) { + if (shouldRedirect(context)) { + context.getExternalContext().requestFlowExecutionRedirect(); + if (popup) { + context.getExternalContext().requestRedirectInPopup(); + } + } else { + View view = viewFactory.getView(context); + render(context, view); } - } else { - View view = viewFactory.getView(context); - render(context, view); } } @@ -182,11 +184,13 @@ public class ViewState extends TransitionableState { logger.debug("Event '" + event.getId() + "' signaled on view " + view); } boolean stateExited = context.handleEvent(event); - if (!stateExited) { + if (!stateExited && context.getExternalContext().isResponseAllowed()) { render(context, view); } } else { - render(context, view); + if (context.getExternalContext().isResponseAllowed()) { + render(context, view); + } } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/portlet/FlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/portlet/FlowHandler.java new file mode 100644 index 00000000..5fded1da --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/portlet/FlowHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.webflow.portlet; + +import javax.portlet.PortletRequest; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import org.springframework.web.portlet.ModelAndView; +import org.springframework.webflow.core.collection.AttributeMap; +import org.springframework.webflow.core.collection.MutableAttributeMap; + +/** + * A controller helper used for customizing access to a single flow definition. This helper is used to: + *