/*
* 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.portlet;
import java.io.IOException;
import java.io.Writer;
import java.security.Principal;
import java.util.Locale;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.MimeResponse;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
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 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;
protected static final short RESOURCE_PHASE = 3;
/**
* The context.
*/
private PortletContext context;
/**
* The request.
*/
private PortletRequest request;
/**
* The response.
*/
private PortletResponse response;
/**
* The portlet request phase: render, action, resource
*/
private short requestPhase;
/**
* 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;
/**
* 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);
}
// 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 false;
}
public String getFlowExecutionUrl(String flowId, String flowExecutionKey) {
if (isRenderPhase()) {
return flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, (RenderResponse) response);
} else if (isResourcePhase()) {
return flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, (ResourceResponse) response);
} else {
throw new IllegalStateException(
"A flow execution action URL can only be obtained in a RenderRequest or a ResourceRequest");
}
}
public Writer getResponseWriter() throws IllegalStateException {
assertResponseAllowed();
try {
return ((MimeResponse) response).getWriter();
} catch (IOException e) {
IllegalStateException ise = new IllegalStateException("Unable to access the response Writer");
ise.initCause(e);
throw ise;
}
}
public boolean isResponseAllowed() {
return (isRenderPhase() || isResourcePhase()) && !responseComplete;
}
public boolean isResponseComplete() {
return responseComplete;
}
public void recordResponseComplete() {
responseComplete = true;
}
public boolean isResponseCompleteFlowExecutionRedirect() {
return flowExecutionRedirectRequested;
}
public void requestFlowExecutionRedirect() throws IllegalStateException {
assertRedirectResponseAllowed();
flowExecutionRedirectRequested = true;
recordResponseComplete();
}
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap<?> input) throws IllegalStateException {
assertRedirectResponseAllowed();
flowDefinitionRedirectFlowId = flowId;
flowDefinitionRedirectFlowInput = new LocalAttributeMap<Object>();
if (input != null) {
flowDefinitionRedirectFlowInput.putAll(input);
}
recordResponseComplete();
}
public void requestExternalRedirect(String uri) throws IllegalStateException {
assertRedirectResponseAllowed();
externalRedirectUrl = uri;
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;
}
/**
* 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;
}
/**
* Returns true if the current request phase is the resource phase
*/
public boolean isResourcePhase() {
return requestPhase == RESOURCE_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<Object>(new PortletRequestMap(request));
this.sessionMap = new LocalSharedAttributeMap<Object>(new PortletSessionMap(request));
this.applicationMap = new LocalSharedAttributeMap<Object>(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 if (request instanceof ResourceRequest && response instanceof ResourceResponse) {
requestPhase = RESOURCE_PHASE;
} else {
throw new IllegalArgumentException("Unknown portlet phase, expected: action, render, or resource");
}
}
private void assertResponseAllowed() throws IllegalStateException {
if (!isRenderPhase() && !isResourcePhase()) {
throw new IllegalStateException(
"A response is not allowed because the current PortletRequest is neither a RenderRequest nor a ResourceRequest");
}
if (responseComplete) {
throw new IllegalStateException(
"A response is not allowed because recordResponseComplete() has already been called on this ExternalContext");
}
}
private void assertRedirectResponseAllowed() throws IllegalStateException {
if (!isActionPhase()) {
throw new IllegalStateException(
"A redirect is not allowed because the current PortletRequest is not a ActionRequest");
}
if (responseComplete) {
throw new IllegalStateException(
"A redirect is not allowed because a response has already been completed on this ExternalContext");
}
}
private boolean isRedirectRequested() {
return getFlowExecutionRedirectRequested() || getFlowDefinitionRedirectRequested()
|| getExternalRedirectRequested();
}
}