first pass at portlet integration
This commit is contained in:
Scott Andrews
2008-03-25 22:42:27 +00:00
parent d1017149f4
commit 5ba34a8321
24 changed files with 1462 additions and 12 deletions

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}
}

View File

@@ -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.
* <p>
* 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;
}
}

View File

@@ -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;

View File

@@ -207,6 +207,10 @@ public class ServletExternalContext implements ExternalContext {
return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested();
}
public boolean isResponseAllowed() {
return true;
}
public void requestFlowExecutionRedirect() {
flowExecutionRedirectRequested = true;
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -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);
}
}
}

View File

@@ -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 <i>single</i> flow definition. This helper is used to:
* <ol>
* <li>Launch executions of that flow with data in the execution input map
* <li>Handle outcomes reached by that flow in a custom manner.
* <li>Handle un-handled exceptions dealing with that flow in a custom manner.
* </ol>
* Such a handler can be visually thought of as a "flow reference" on a Garrett IA diagram. It holds a reference to the
* flow id to launch, how to provision its input, how to process its outcomes, and how to handle uncaught exceptions.
*
* @author Keith Donald
*/
public interface FlowHandler {
/**
* Returns the id of the flow handled by this handler. Used by a Controller to load the flow definition.
* @return the flow id
*/
public String getFlowId();
/**
* Creates the flow execution input map to pass to a new instance of the flow being started. Used by a Controller to
* launch the flow execution with the correct input.
* @param request the current request
* @return the input map
*/
public MutableAttributeMap createExecutionInputMap(PortletRequest request);
public ModelAndView handleFlowOutcome(String endedOutcome, AttributeMap endedOutput, RenderRequest request,
RenderResponse response);
}

View File

@@ -0,0 +1,77 @@
package org.springframework.webflow.portlet;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import org.springframework.web.portlet.HandlerAdapter;
import org.springframework.web.portlet.ModelAndView;
import org.springframework.web.portlet.context.PortletApplicationObjectSupport;
import org.springframework.webflow.context.portlet.FlowUrlHandler;
import org.springframework.webflow.context.portlet.PortletExternalContext;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.executor.FlowExecutionResult;
import org.springframework.webflow.executor.FlowExecutor;
public class FlowHandlerAdapter extends PortletApplicationObjectSupport implements HandlerAdapter {
private static final String FLOW_EXECUTION_RESULT = "flowExecutionResult";
private FlowExecutor flowExecutor;
private FlowUrlHandler urlHandler;
public FlowHandlerAdapter(FlowExecutor flowExecutor) {
this.flowExecutor = flowExecutor;
}
public ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception {
FlowHandler flowHandler = (FlowHandler) handler;
String flowExecutionKey = urlHandler.getFlowExecutionKey(request);
if (flowExecutionKey != null) {
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.resumeExecution(flowExecutionKey, context);
return null;
} else {
PortletSession session = request.getPortletSession(true);
FlowExecutionResult result = (FlowExecutionResult) session.getAttribute(FLOW_EXECUTION_RESULT);
if (result != null) {
session.removeAttribute(FLOW_EXECUTION_RESULT);
return flowHandler.handleFlowOutcome(result.getEndedOutcome(), result.getEndedOutput(), request,
response);
} else {
MutableAttributeMap input = flowHandler.createExecutionInputMap(request);
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.launchExecution(flowHandler.getFlowId(), input, context);
return null;
}
}
}
public void handleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception {
String flowExecutionKey = urlHandler.getFlowExecutionKey(request);
PortletExternalContext context = createPortletExternalContext(request, response);
FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
if (result.paused()) {
urlHandler.setFlowExecutionRenderParameter(flowExecutionKey, response);
} else {
setEndResult(result, request);
}
}
private void setEndResult(FlowExecutionResult result, ActionRequest request) {
request.getPortletSession().setAttribute(FLOW_EXECUTION_RESULT, result);
}
public boolean supports(Object handler) {
return handler instanceof FlowHandler;
}
private PortletExternalContext createPortletExternalContext(PortletRequest request, PortletResponse response) {
return new PortletExternalContext(getPortletContext(), request, response);
}
}

View File

@@ -145,6 +145,10 @@ public class MockExternalContext implements ExternalContext {
return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested();
}
public boolean isResponseAllowed() {
return true;
}
public void requestFlowExecutionRedirect() {
flowExecutionRedirectRequested = true;
}

View File

@@ -0,0 +1,29 @@
package org.springframework.webflow.context.portlet;
import junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockActionResponse;
import org.springframework.mock.web.portlet.MockPortletRequest;
import org.springframework.mock.web.portlet.MockRenderResponse;
public class DefaultFlowUrlHandlerTests extends TestCase {
private DefaultFlowUrlHandler urlHandler = new DefaultFlowUrlHandler();
private MockPortletRequest request = new MockPortletRequest();
private MockActionResponse actionResponse = new MockActionResponse();
private MockRenderResponse renderResponse = new MockRenderResponse();
public void testGetFlowExecutionKey() {
request.addParameter("execution", "12345");
assertEquals("12345", urlHandler.getFlowExecutionKey(request));
}
public void testSetFlowExecutionRenderParameter() {
urlHandler.setFlowExecutionRenderParameter("12345", actionResponse);
assertEquals("12345", actionResponse.getRenderParameter("execution"));
}
public void testCreateFlowExecutionUrl() {
String url = urlHandler.createFlowExecutionUrl("foo", "12345", renderResponse);
assertEquals("http://localhost/mockportlet?urlType=action;param_execution=12345", url);
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockPortletContext;
/**
* Test case for the {@link PortletContextMap} class.
*
* @author Ulrik Sandberg
* @author Erwin Vervaet
* @author Scott Andrews
*/
public class PortletContextMapTests extends TestCase {
private PortletContextMap tested;
private MockPortletContext context;
protected void setUp() throws Exception {
super.setUp();
context = new MockPortletContext();
// a fresh MockPortletContext seems to already contain an element;
// that's confusing, so we remove it
context.removeAttribute("javax.servlet.context.tempdir");
tested = new PortletContextMap(context);
tested.put("SomeKey", "SomeValue");
}
protected void tearDown() throws Exception {
super.tearDown();
context = null;
tested = null;
}
public void testIsEmpty() {
tested.remove("SomeKey");
assertEquals("size,", 0, tested.size());
assertEquals("isEmpty,", true, tested.isEmpty());
}
public void testSizeAddOne() {
assertEquals("size,", 1, tested.size());
}
public void testSizeAddTwo() {
tested.put("SomeOtherKey", "SomeOtherValue");
assertEquals("size,", 2, tested.size());
}
public void testContainsKey() {
assertEquals("containsKey,", true, tested.containsKey("SomeKey"));
}
public void testContainsValue() {
assertTrue(tested.containsValue("SomeValue"));
}
public void testGet() {
assertEquals("get,", "SomeValue", tested.get("SomeKey"));
}
public void testPut() {
Object old = tested.put("SomeKey", "SomeNewValue");
assertEquals("old value,", "SomeValue", old);
assertEquals("new value,", "SomeNewValue", tested.get("SomeKey"));
}
public void testRemove() {
Object old = tested.remove("SomeKey");
assertEquals("old value,", "SomeValue", old);
assertNull("should be gone", tested.get("SomeKey"));
}
public void testPutAll() {
Map otherMap = new HashMap();
otherMap.put("SomeOtherKey", "SomeOtherValue");
otherMap.put("SomeKey", "SomeUpdatedValue");
tested.putAll(otherMap);
assertEquals("SomeOtherValue", tested.get("SomeOtherKey"));
assertEquals("SomeUpdatedValue", tested.get("SomeKey"));
}
public void testClear() {
tested.clear();
assertTrue(tested.isEmpty());
}
public void testKeySet() {
assertEquals(1, tested.keySet().size());
assertTrue(tested.keySet().contains("SomeKey"));
}
public void testValues() {
assertEquals(1, tested.values().size());
assertTrue(tested.values().contains("SomeValue"));
}
public void testEntrySet() {
assertEquals(1, tested.entrySet().size());
assertEquals("SomeKey", ((Entry) tested.entrySet().iterator().next()).getKey());
assertEquals("SomeValue", ((Entry) tested.entrySet().iterator().next()).getValue());
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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 junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockActionRequest;
import org.springframework.mock.web.portlet.MockActionResponse;
import org.springframework.mock.web.portlet.MockPortletContext;
import org.springframework.mock.web.portlet.MockPortletRequest;
import org.springframework.mock.web.portlet.MockPortletResponse;
import org.springframework.webflow.context.servlet.ServletExternalContext;
/**
* Unit tests for {@link ServletExternalContext}.
*/
public class PortletExternalContextTests extends TestCase {
private MockPortletContext portletContext;
private MockPortletRequest request;
private MockPortletResponse response;
private PortletExternalContext context;
protected void setUp() {
portletContext = new MockPortletContext();
request = new MockActionRequest();
response = new MockActionResponse();
context = new PortletExternalContext(portletContext, request, response);
}
public void testGetContextPath() {
request.setContextPath("/foo");
assertEquals("/foo", request.getContextPath());
}
public void testRequestParameters() {
assertTrue(context.getRequestParameterMap().isEmpty());
}
public void testGetNativeObjects() {
assertEquals(portletContext, context.getNativeContext());
assertEquals(request, context.getNativeRequest());
assertEquals(response, context.getNativeResponse());
}
public void testNotAnAjaxRequest() {
assertFalse(context.isAjaxRequest());
}
public void testAjaxRequestAcceptHeader() {
context.setAjaxRequest(true);
assertTrue(context.isAjaxRequest());
}
public void testNotResponseCommitted() {
assertFalse(context.isResponseCommitted());
}
public void testCommitExecutionRedirect() {
context.requestFlowExecutionRedirect();
assertTrue(context.isResponseCommitted());
assertTrue(context.flowExecutionRedirectRequested());
}
public void testCommitFlowRedirect() {
context.requestFlowDefinitionRedirect("foo", null);
assertTrue(context.isResponseCommitted());
assertTrue(context.flowDefinitionRedirectRequested());
assertEquals("foo", context.getFlowRedirectFlowId());
}
public void testCommitExternalRedirect() {
context.requestExternalRedirect("foo");
assertTrue(context.isResponseCommitted());
assertTrue(context.externalRedirectRequested());
assertEquals("foo", context.getExternalRedirectUrl());
}
public void testCommitExecutionRedirectPopup() {
context.requestFlowExecutionRedirect();
context.requestRedirectInPopup();
assertTrue(context.isResponseCommitted());
assertTrue(context.flowExecutionRedirectRequested());
assertTrue(context.redirectInPopup());
}
public void testCommitFlowRedirectPopup() {
context.requestFlowDefinitionRedirect("foo", null);
context.requestRedirectInPopup();
assertTrue(context.isResponseCommitted());
assertTrue(context.flowDefinitionRedirectRequested());
assertEquals("foo", context.getFlowRedirectFlowId());
assertTrue(context.redirectInPopup());
}
public void testCommitExternalRedirectPopup() {
context.requestExternalRedirect("foo");
context.requestRedirectInPopup();
assertTrue(context.isResponseCommitted());
assertTrue(context.externalRedirectRequested());
assertEquals("foo", context.getExternalRedirectUrl());
assertTrue(context.redirectInPopup());
}
public void testResponseAllowed() {
assertFalse(context.isResponseAllowed());
}
public void testIsActionPhase() {
assertTrue(context.isActionPhase());
}
public void testIsRenderPhase() {
assertFalse(context.isRenderPhase());
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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 junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockPortletRequest;
/**
* Unit test for the {@link PortletRequestMap} class.
*
* @author Ulrik Sandberg
* @author Scott Andrews
*/
public class PortletRequestMapTests extends TestCase {
private PortletRequestMap tested;
private MockPortletRequest request;
protected void setUp() throws Exception {
super.setUp();
request = new MockPortletRequest();
tested = new PortletRequestMap(request);
}
protected void tearDown() throws Exception {
super.tearDown();
request = null;
tested = null;
}
public void testGetAttribute() {
request.setAttribute("Some key", "Some value");
// perform test
Object result = tested.getAttribute("Some key");
assertEquals("Some value", result);
}
public void testSetAttribute() {
// perform test
tested.setAttribute("Some key", "Some value");
assertEquals("Some value", request.getAttribute("Some key"));
}
public void testRemoveAttribute() {
request.setAttribute("Some key", "Some value");
// perform test
tested.removeAttribute("Some key");
assertNull(request.getAttribute("Some key"));
}
public void testGetAttributeNames() {
request.setAttribute("Some key", "Some value");
request.removeAttribute("javax.servlet.context.tempdir");
// perform test
Iterator names = tested.getAttributeNames();
assertNotNull("Null result unexpected", names);
assertTrue("More elements", names.hasNext());
String name = (String) names.next();
assertEquals("Some key", name);
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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 junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockPortletRequest;
/**
* Unit test for the {@link PortletRequestParameterMap} class.
*
* @author Ulrik Sandberg
* @author Scott Andrews
*/
public class PortletRequestParameterMapTests extends TestCase {
private PortletRequestParameterMap tested;
private MockPortletRequest request;
protected void setUp() throws Exception {
super.setUp();
request = new MockPortletRequest();
tested = new PortletRequestParameterMap(request);
}
protected void tearDown() throws Exception {
super.tearDown();
request = null;
tested = null;
}
public void testGetAttribute() {
request.setParameter("Some param", "Some value");
// perform test
Object result = tested.getAttribute("Some param");
assertEquals("Some value", result);
}
public void testSetAttribute() {
// perform test
try {
tested.setAttribute("Some key", "Some value");
fail("UnsupportedOperationException expected");
} catch (UnsupportedOperationException expected) {
// expected
}
}
public void testRemoveAttribute() {
request.setParameter("Some param", "Some value");
// perform test
try {
tested.removeAttribute("Some param");
fail("UnsupportedOperationException expected");
} catch (UnsupportedOperationException expected) {
// expected
}
}
public void testGetAttributeNames() {
request.setParameter("Some param", "Some value");
// perform test
Iterator names = tested.getAttributeNames();
assertNotNull("Null result unexpected", names);
assertTrue("More elements", names.hasNext());
String name = (String) names.next();
assertEquals("Some param", name);
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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 junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockPortletRequest;
import org.springframework.web.util.WebUtils;
/**
* Unit test for the {@link PortletSessionMap} class.
*
* @author Ulrik Sandberg
* @author Scott Andrews
*/
public class PortletSessionMapTests extends TestCase {
private PortletSessionMap tested;
private MockPortletRequest request;
protected void setUp() throws Exception {
super.setUp();
request = new MockPortletRequest();
tested = new PortletSessionMap(request);
}
protected void tearDown() throws Exception {
super.tearDown();
request = null;
tested = null;
}
public void testGetAttribute() {
request.getPortletSession().setAttribute("Some key", "Some value");
// perform test
Object result = tested.getAttribute("Some key");
assertEquals("Some value", result);
}
public void testGetAttributeNullSession() {
request.setSession(null);
// perform test
Object result = tested.getAttribute("Some key");
assertNull("No value expected", result);
}
public void testSetAttribute() {
// perform test
tested.setAttribute("Some key", "Some value");
assertEquals("Some value", request.getPortletSession().getAttribute("Some key"));
}
public void testRemoveAttribute() {
request.getPortletSession().setAttribute("Some key", "Some value");
// perform test
tested.removeAttribute("Some key");
assertNull(request.getPortletSession().getAttribute("Some key"));
}
public void testRemoveAttributeNullSession() {
request.setSession(null);
// perform test
tested.removeAttribute("Some key");
assertNull(request.getPortletSession().getAttribute("Some key"));
}
public void testGetAttributeNames() {
request.getPortletSession().setAttribute("Some key", "Some value");
// perform test
Iterator names = tested.getAttributeNames();
assertNotNull("Null result unexpected", names);
assertTrue("More elements", names.hasNext());
String name = (String) names.next();
assertEquals("Some key", name);
}
public void testGetAttributeNamesNullSession() {
request.setSession(null);
// perform test
Iterator names = tested.getAttributeNames();
assertNotNull("Null result unexpected", names);
assertFalse("No elements expected", names.hasNext());
}
public void testGetSessionAsMutex() {
Object mutex = tested.getMutex();
assertSame(mutex, request.getPortletSession());
}
public void testGetSessionMutex() {
Object object = new Object();
request.getPortletSession().setAttribute(WebUtils.SESSION_MUTEX_ATTRIBUTE, object);
Object mutex = tested.getMutex();
assertSame(mutex, object);
}
}

View File

@@ -115,4 +115,8 @@ public class ServletExternalContextTests extends TestCase {
assertTrue(context.redirectInPopup());
}
public void testResponseAllowed() {
assertTrue(context.isResponseAllowed());
}
}

View File

@@ -13,7 +13,7 @@
* See the 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@@ -21,6 +21,7 @@ import javax.servlet.http.HttpSession;
import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.webflow.context.servlet.HttpSessionMap;
import org.springframework.webflow.core.collection.AttributeMapBindingEvent;
import org.springframework.webflow.core.collection.AttributeMapBindingListener;