/* * Copyright 2004-2012 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.servlet; import java.io.IOException; import java.io.Writer; import java.security.Principal; import java.util.Locale; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.webflow.context.ExternalContext; 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 HTTP Servlet environment that has interacted with Spring Web Flow. * * @author Keith Donald * @author Erwin Vervaet * @author Jeremy Grelle */ public class ServletExternalContext implements ExternalContext { /** * The context. */ private ServletContext context; /** * The request. */ private HttpServletRequest request; /** * The response. */ private HttpServletResponse response; /** * An accessor for the HTTP request parameter map. */ private ParameterMap requestParameterMap; /** * An accessor for the HTTP request attribute map. */ private MutableAttributeMap<Object> requestMap; /** * An accessor for the HTTP session map. */ private SharedAttributeMap<Object> sessionMap; /** * An accessor for the servlet context application map. */ private SharedAttributeMap<Object> applicationMap; /** * A flag indicating if the flow committed the response. Set to true by requesting an execution redirect, definition * redirect, external redirect, or by calling {@link ExternalContext#recordResponseComplete()} */ private boolean responseComplete; /** * 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 MutableAttributeMap<Object> 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 servlet HTTP request and response and given servlet context. * @param context the servlet context * @param request the http servlet request * @param response the http servlet response */ public ServletExternalContext(ServletContext context, HttpServletRequest request, HttpServletResponse response) { init(context, request, response, new DefaultFlowUrlHandler()); } /** * Create a new external context wrapping given servlet HTTP request and response and given servlet context. * @param context the servlet context * @param request the http servlet request * @param response the http servlet response * @param flowUrlHandler the flow url handler */ public ServletExternalContext(ServletContext context, HttpServletRequest request, HttpServletResponse 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<Object> getRequestMap() { return requestMap; } public SharedAttributeMap<Object> getSessionMap() { return sessionMap; } public SharedAttributeMap<Object> getGlobalSessionMap() { return getSessionMap(); } public SharedAttributeMap<Object> getApplicationMap() { return applicationMap; } public Principal getCurrentUser() { return request.getUserPrincipal(); } public Locale getLocale() { return request.getLocale(); } public Object getNativeContext() { return context; } public Object getNativeRequest() { return request; } public Object getNativeResponse() { return response; } public boolean isAjaxRequest() { return ajaxRequest; } public String getFlowExecutionUrl(String flowId, String flowExecutionKey) { return response.encodeURL(flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, request)); } public Writer getResponseWriter() throws IllegalStateException { assertResponseAllowed(); try { return response.getWriter(); } catch (IOException e) { IllegalStateException ise = new IllegalStateException("Unable to access the response Writer"); ise.initCause(e); throw ise; } } public boolean isResponseAllowed() { return !responseComplete; } public boolean isResponseComplete() { return responseComplete; } public void recordResponseComplete() { responseComplete = true; } public boolean isResponseCompleteFlowExecutionRedirect() { return flowExecutionRedirectRequested; } public void requestFlowExecutionRedirect() throws IllegalStateException { assertResponseAllowed(); flowExecutionRedirectRequested = true; recordResponseComplete(); } public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap<?> input) throws IllegalStateException { assertResponseAllowed(); flowDefinitionRedirectFlowId = flowId; flowDefinitionRedirectFlowInput = new LocalAttributeMap<Object>(); if (input != null) { flowDefinitionRedirectFlowInput.putAll(input); } recordResponseComplete(); } public void requestExternalRedirect(String location) throws IllegalStateException { assertResponseAllowed(); externalRedirectUrl = location; recordResponseComplete(); } public void requestRedirectInPopup() throws IllegalStateException { if (isRedirectRequested()) { redirectInPopup = true; } else { throw new IllegalStateException( "Only call requestRedirectInPopup after a redirect has been requested by calling requestFlowExecutionRedirect, requestFlowDefinitionRedirect, or requestExternalRedirect"); } } // implementation specific methods /** * Returns the flag indicating if a flow execution redirect response has been requested by the flow. */ public boolean getFlowExecutionRedirectRequested() { return flowExecutionRedirectRequested; } /** * Returns the flag indicating if a flow definition redirect response has been requested by the flow. */ public boolean getFlowDefinitionRedirectRequested() { return flowDefinitionRedirectFlowId != null; } /** * Returns the id of the flow definition to redirect to. Only set when {@link #getFlowDefinitionRedirectRequested()} * returns true. */ public String getFlowRedirectFlowId() { return flowDefinitionRedirectFlowId; } /** * Returns the input to pass the flow definition through the redirect. Only set when * {@link #getFlowDefinitionRedirectRequested()} returns true. */ public MutableAttributeMap<Object> getFlowRedirectFlowInput() { return flowDefinitionRedirectFlowInput; } /** * Returns the flag indicating if an external redirect response has been requested by the flow. */ public boolean getExternalRedirectRequested() { return externalRedirectUrl != null; } /** * Returns the URL to redirect to. Only set if {@link #getExternalRedirectRequested()} 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 getRedirectInPopup() { return redirectInPopup; } // hooks for subclasses /** * Returns the servlet context. */ protected ServletContext getContext() { return context; } /** * Returns the underlying HttpServletRequest. */ protected HttpServletRequest getRequest() { return request; } /** * Returns the underlying HttpServletResponse. */ protected HttpServletResponse getResponse() { return response; } /** * Returns the configured flow url handler. */ protected FlowUrlHandler getFlowUrlHandler() { return flowUrlHandler; } // private helpers private void init(ServletContext context, HttpServletRequest request, HttpServletResponse response, FlowUrlHandler flowUrlHandler) { this.context = context; this.request = request; this.response = response; this.requestParameterMap = new LocalParameterMap(new HttpServletRequestParameterMap(request)); this.requestMap = new LocalAttributeMap<Object>(new HttpServletRequestMap(request)); this.sessionMap = new LocalSharedAttributeMap<Object>(new HttpSessionMap(request)); this.applicationMap = new LocalSharedAttributeMap<Object>(new HttpServletContextMap(context)); this.flowUrlHandler = flowUrlHandler; } private void assertResponseAllowed() throws IllegalStateException { if (!isResponseAllowed()) { if (getFlowExecutionRedirectRequested()) { throw new IllegalStateException( "A response is not allowed because a redirect has already been requested on this ExternalContext"); } if (getFlowDefinitionRedirectRequested()) { throw new IllegalStateException( "A response is not allowed because a flowRedirect has already been requested on this ExternalContext"); } if (getExternalRedirectRequested()) { throw new IllegalStateException( "A response is not allowed because an externalRedirect has already been requested on this ExternalContext"); } throw new IllegalStateException( "A response is not allowed because one has already been completed on this ExternalContext"); } } private boolean isRedirectRequested() { return getFlowExecutionRedirectRequested() || getFlowDefinitionRedirectRequested() || getExternalRedirectRequested(); } }