SWF-1379 Remove dependency on the Portlet bridge for JSF and provide basic support within Web Flow for working Portlets and JSF. Update portlet samples

This commit is contained in:
Rossen Stoyanchev
2010-09-13 14:57:14 +00:00
parent 7986cbdb0e
commit 9e2137d3c6
32 changed files with 1448 additions and 348 deletions

View File

@@ -30,8 +30,12 @@ import javax.faces.context.ResponseStream;
import javax.faces.context.ResponseWriter;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.render.RenderKit;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import org.springframework.context.MessageSource;
import org.springframework.faces.webflow.context.portlet.PortletFacesContextImpl;
import org.springframework.util.ClassUtils;
import org.springframework.webflow.execution.RequestContext;
@@ -66,11 +70,18 @@ public class FlowFacesContext extends FacesContext {
private FacesContext delegate;
public static FlowFacesContext newInstance(RequestContext context, Lifecycle lifecycle) {
FacesContextFactory facesContextFactory = (FacesContextFactory) FactoryFinder
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
FacesContext defaultFacesContext = facesContextFactory.getFacesContext(context.getExternalContext()
.getNativeContext(), context.getExternalContext().getNativeRequest(), context.getExternalContext()
.getNativeResponse(), lifecycle);
FacesContext defaultFacesContext = null;
if (JsfRuntimeInformation.isPortletRequest(context)) {
defaultFacesContext = new PortletFacesContextImpl((PortletContext) context.getExternalContext()
.getNativeContext(), (PortletRequest) context.getExternalContext().getNativeRequest(),
(PortletResponse) context.getExternalContext().getNativeResponse());
} else {
FacesContextFactory facesContextFactory = (FacesContextFactory) FactoryFinder
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
defaultFacesContext = facesContextFactory.getFacesContext(context.getExternalContext().getNativeContext(),
context.getExternalContext().getNativeRequest(), context.getExternalContext().getNativeResponse(),
lifecycle);
}
return (JsfRuntimeInformation.isAtLeastJsf20()) ? new Jsf2FlowFacesContext(context, defaultFacesContext)
: new FlowFacesContext(context, defaultFacesContext);
}

View File

@@ -19,6 +19,7 @@ import javax.faces.context.FacesContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.webflow.execution.RequestContext;
/**
* Helper class to provide information about the JSF runtime environment such as JSF version and implementation.
@@ -63,7 +64,7 @@ public class JsfRuntimeInformation {
return jsfVersion < JSF_20;
}
protected static boolean isMyFacesPresent() {
public static boolean isMyFacesPresent() {
return myFacesPresent;
}
@@ -71,4 +72,9 @@ public class JsfRuntimeInformation {
return context.getExternalContext().getContext().getClass().getName().indexOf("Portlet") != -1;
}
public static boolean isPortletRequest(RequestContext context) {
return (null != ClassUtils.getMethodIfAvailable(context.getExternalContext().getNativeContext().getClass(),
"getPortletContextName"));
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2004-2010 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.faces.webflow.application.portlet;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKit;
import javax.portlet.RenderResponse;
import org.springframework.faces.webflow.JsfRuntimeInformation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.sun.facelets.FaceletViewHandler;
/**
* <p>
* This {@link ViewHandler} implementation is needed because portions of the native Facelets ViewHandler depend on the
* Servlet API and cannot be used directly in a Portlet environment.
* </p>
*
* <p>
* Note: the basis for this code was a Facelets sample provided with Apache MyFaces Portlet Bridge for JSF version
* 2.0.0.alpha-2.
* </p>
*
* @since 2.2.0
*/
public class PortletFaceletViewHandler extends FaceletViewHandler {
private static final String FACELETS_CONTENT_TYPE_KEY = "facelets.ContentType";
private static final String FACELETS_ENCODING_KEY = "facelets.Encoding";
public PortletFaceletViewHandler(ViewHandler parent) {
super(parent);
}
protected ResponseWriter createResponseWriter(FacesContext context) throws IOException, FacesException {
if (!JsfRuntimeInformation.isPortletRequest(context)) {
return super.createResponseWriter(context);
}
// Create a temporary ResponseWriter to see what content type the ReponseWriter is going to ask for.
ResponseWriter writer = createNoopResponseWriter(context);
RenderResponse response = (RenderResponse) context.getExternalContext().getResponse();
String contentType = getResponseContentType(context, writer.getContentType());
String encoding = getResponseEncoding(context, writer.getCharacterEncoding());
// Set the content type and the encoding and clone writer with the real ResponseWriter
response.setContentType(contentType + "; charset=" + encoding);
return writer.cloneWithWriter(response.getWriter());
}
private ResponseWriter createNoopResponseWriter(FacesContext context) {
RenderKit renderKit = context.getRenderKit();
Assert.notNull(renderKit, context.getViewRoot().getRenderKitId());
// Append */* to the contentType so createResponseWriter will succeed no matter the requested contentType.
String contentType = (String) context.getExternalContext().getRequestMap().get(FACELETS_CONTENT_TYPE_KEY);
if (StringUtils.hasText(contentType) && (!contentType.equals("*/*"))) {
contentType += ",*/*";
}
ResponseWriter writer;
String encoding = (String) context.getExternalContext().getRequestMap().get(FACELETS_ENCODING_KEY);
try {
writer = renderKit.createResponseWriter(NoopWriter.INSTANCE, contentType, encoding);
} catch (IllegalArgumentException e) {
// See RI bug prior to 1.2_05-b3. Might as well leave it:
// https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=613
log.fine("The impl didn't correctly handle '*/*' in the content type list.. try '*/*' directly.");
writer = renderKit.createResponseWriter(NoopWriter.INSTANCE, "*/*", encoding);
}
return writer;
}
@SuppressWarnings("unchecked")
protected String getResponseEncoding(FacesContext context, String originalEncoding) {
String encoding = originalEncoding;
Map requestMap = context.getExternalContext().getRequestMap();
Map sessionMap = context.getExternalContext().getSessionMap();
// 1. check the request attribute
if (requestMap.containsKey(FACELETS_ENCODING_KEY)) {
encoding = (String) requestMap.get(FACELETS_ENCODING_KEY);
sessionMap.put(CHARACTER_ENCODING_KEY, encoding);
}
// 2. get it from request
if (encoding == null) {
encoding = context.getExternalContext().getResponseCharacterEncoding();
}
// 3. get it from the session
if (encoding == null) {
encoding = (String) sessionMap.get(CHARACTER_ENCODING_KEY);
}
// 4. default it
if (encoding == null) {
encoding = "UTF-8";
}
return encoding;
}
protected static class NoopWriter extends Writer {
static final NoopWriter INSTANCE = new NoopWriter();
public void write(char[] buffer) {
}
public void write(char[] buffer, int off, int len) {
}
public void write(String str) {
}
public void write(int c) {
}
public void write(String str, int off, int len) {
}
public void close() {
}
public void flush() {
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2004-2010 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.faces.webflow.context.portlet;
import java.util.Iterator;
import javax.portlet.PortletContext;
import org.springframework.binding.collection.StringKeyedMapAdapter;
import org.springframework.webflow.core.collection.CollectionUtils;
/**
* Map backed by a PortletContext for accessing Portlet initialization parameters.
*
* @author Rossen Stoyanchev
* @since 2.2.0
*/
public class InitParameterMap extends StringKeyedMapAdapter {
final private PortletContext portletContext;
public InitParameterMap(PortletContext portletContext) {
this.portletContext = portletContext;
}
@Override
protected String getAttribute(String key) {
return portletContext.getInitParameter(key);
}
@Override
protected void setAttribute(String key, Object value) {
throw new UnsupportedOperationException("Cannot set PortletContext InitParameter");
}
@Override
protected void removeAttribute(String key) {
throw new UnsupportedOperationException("Cannot remove PortletContext InitParameter");
}
@Override
@SuppressWarnings("unchecked")
protected Iterator<String> getAttributeNames() {
return CollectionUtils.toIterator(portletContext.getInitParameterNames());
}
}

View File

@@ -0,0 +1,393 @@
/*
* Copyright 2004-2010 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.faces.webflow.context.portlet;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Principal;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.faces.FacesException;
import javax.faces.context.ExternalContext;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import org.springframework.binding.collection.MapAdaptable;
import org.springframework.faces.webflow.JsfRuntimeInformation;
import org.springframework.util.Assert;
import org.springframework.webflow.context.portlet.PortletContextMap;
import org.springframework.webflow.context.portlet.PortletRequestMap;
import org.springframework.webflow.context.portlet.PortletSessionMap;
import org.springframework.webflow.core.collection.CollectionUtils;
import org.springframework.webflow.core.collection.LocalAttributeMap;
/**
* An implementation of {@link ExternalContext} for use with Portlet requests.
*
* @author Rossen Stoyanchev
* @since 2.2.0
*/
public class PortletExternalContextImpl extends ExternalContext {
private ActionRequest actionRequest;
private Map<String, Object> applicationMap;
private boolean isActionRequest;
private PortletContext portletContext;
private PortletRequest portletRequest;
private PortletResponse portletResponse;
private Map<String, String> initParameterMap;
private Map<String, String> requestHeaderMap;
private Map<String, String[]> requestHeaderValuesMap;
private Map<String, Object> requestMap;
private Map<String, String> requestParameterMap;
private Map<String, String[]> requestParameterValuesMap;
private MapAdaptable sessionMap;
public PortletExternalContextImpl(PortletContext portletContext, PortletRequest portletRequest,
PortletResponse portletResponse) {
this.portletContext = portletContext;
this.portletRequest = portletRequest;
this.portletResponse = portletResponse;
if (portletRequest instanceof ActionRequest) {
this.actionRequest = (ActionRequest) portletRequest;
this.isActionRequest = true;
}
}
public void dispatch(String path) throws IOException {
Assert.isTrue(!isActionRequest);
PortletRequestDispatcher requestDispatcher = portletContext.getRequestDispatcher(path);
try {
requestDispatcher.include((RenderRequest) portletRequest, (RenderResponse) portletResponse);
} catch (PortletException exception) {
if (exception.getMessage() != null) {
throw new FacesException(exception.getMessage(), exception);
}
throw new FacesException(exception);
}
}
public String encodeActionURL(String url) {
Assert.notNull(url);
return portletResponse.encodeURL(url);
}
public String encodeNamespace(String name) {
Assert.isTrue(!isActionRequest);
return name + ((RenderResponse) portletResponse).getNamespace();
}
@Override
public String encodeResourceURL(String url) {
Assert.notNull(url);
return portletResponse.encodeURL(url);
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> getApplicationMap() {
if (applicationMap == null) {
applicationMap = new PortletContextMap(portletContext);
}
return applicationMap;
}
@Override
public String getAuthType() {
return portletRequest.getAuthType();
}
@Override
public Object getContext() {
return portletContext;
}
@Override
public String getInitParameter(String name) {
return portletContext.getInitParameter(name);
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String> getInitParameterMap() {
if (initParameterMap == null) {
initParameterMap = new InitParameterMap(portletContext);
}
return initParameterMap;
}
@Override
public String getRemoteUser() {
return portletRequest.getRemoteUser();
}
@Override
public Object getRequest() {
return portletRequest;
}
@Override
public String getRequestContentType() {
return null;
}
@Override
public String getRequestContextPath() {
return portletRequest.getContextPath();
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> getRequestCookieMap() {
return Collections.EMPTY_MAP;
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String> getRequestHeaderMap() {
if (requestHeaderMap == null) {
RequestPropertyMap map = new RequestPropertyMap(portletRequest);
map.setUseArrayForMultiValueAttributes(Boolean.FALSE);
requestHeaderMap = map;
}
return requestHeaderMap;
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String[]> getRequestHeaderValuesMap() {
if (requestHeaderValuesMap == null) {
RequestPropertyMap map = new RequestPropertyMap(portletRequest);
map.setUseArrayForMultiValueAttributes(Boolean.TRUE);
requestHeaderValuesMap = map;
}
return requestHeaderValuesMap;
}
@Override
public Locale getRequestLocale() {
return portletRequest.getLocale();
}
@Override
@SuppressWarnings("unchecked")
public Iterator<Locale> getRequestLocales() {
return CollectionUtils.toIterator(portletRequest.getLocales());
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> getRequestMap() {
if (requestMap == null) {
requestMap = new PortletRequestMap(portletRequest);
}
return requestMap;
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String> getRequestParameterMap() {
if (requestParameterMap == null) {
RequestParameterMap map = new RequestParameterMap(portletRequest);
map.setUseArrayForMultiValueAttributes(Boolean.FALSE);
requestParameterMap = map;
}
return requestParameterMap;
}
@Override
@SuppressWarnings("unchecked")
public Iterator<String> getRequestParameterNames() {
return CollectionUtils.toIterator(portletRequest.getParameterNames());
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String[]> getRequestParameterValuesMap() {
if (requestParameterValuesMap == null) {
RequestParameterMap map = new RequestParameterMap(portletRequest);
map.setUseArrayForMultiValueAttributes(Boolean.TRUE);
requestParameterValuesMap = map;
}
return requestParameterValuesMap;
}
@Override
public String getRequestPathInfo() {
return null;
}
@Override
public String getRequestServletPath() {
//
// Return "" instead of null in order to prevent NullPointerException in Apache MyFaces 1.2 when it tries to
// determine the servlet mappings in DefaultViewHandlerSupport.calculateFacesServletMapping(..).
// Note that the FacesServlet mapping in Web Flow is not relevant so this should be ok.
//
// Alternatively this method could be implemented to provide an actual servlet path derived from the
// viewId when that becomes available during rendering as the MyFaces Portlet Bridge does.
//
return (JsfRuntimeInformation.isMyFacesPresent()) ? "" : null;
}
@Override
public URL getResource(String path) throws MalformedURLException {
Assert.notNull(path);
return portletContext.getResource(path);
}
@Override
public InputStream getResourceAsStream(String path) {
Assert.notNull(path);
return portletContext.getResourceAsStream(path);
}
@Override
public Set<String> getResourcePaths(String path) {
Assert.notNull(path);
return portletContext.getResourcePaths(path);
}
@Override
public Object getResponse() {
return portletResponse;
}
@Override
public String getResponseContentType() {
return null;
}
@Override
public Object getSession(boolean create) {
return portletRequest.getPortletSession(create);
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> getSessionMap() {
if (sessionMap == null) {
sessionMap = new LocalAttributeMap(new PortletSessionMap(portletRequest));
}
return sessionMap.asMap();
}
@Override
public Principal getUserPrincipal() {
return portletRequest.getUserPrincipal();
}
@Override
public boolean isUserInRole(String role) {
Assert.notNull(role);
return portletRequest.isUserInRole(role);
}
@Override
public void log(String message) {
Assert.notNull(message);
portletContext.log(message);
}
@Override
public void log(String message, Throwable exception) {
Assert.notNull(message);
Assert.notNull(exception);
portletContext.log(message, exception);
}
@Override
public void redirect(String url) throws IOException {
if (actionRequest instanceof ActionResponse) {
((ActionResponse) portletResponse).sendRedirect(url);
} else {
throw new IllegalArgumentException("Only ActionResponse supported");
}
}
public void release() {
portletContext = null;
portletRequest = null;
portletResponse = null;
applicationMap = null;
sessionMap = null;
requestMap = null;
requestParameterMap = null;
requestParameterValuesMap = null;
requestHeaderMap = null;
requestHeaderValuesMap = null;
initParameterMap = null;
actionRequest = null;
}
@Override
public void setRequest(Object request) {
this.portletRequest = (PortletRequest) request;
this.actionRequest = (portletRequest instanceof ActionRequest) ? (ActionRequest) request : null;
}
public void setRequestCharacterEncoding(String encoding) throws java.io.UnsupportedEncodingException {
Assert.notNull(actionRequest, "The request be an action request.");
actionRequest.setCharacterEncoding(encoding);
}
@Override
public String getRequestCharacterEncoding() {
Assert.notNull(actionRequest, "The request be an action request.");
return actionRequest.getCharacterEncoding();
}
@Override
public String getResponseCharacterEncoding() {
return null;
}
@Override
public void setResponseCharacterEncoding(String encoding) {
// no-op
}
@Override
public void setResponse(Object response) {
this.portletResponse = (PortletResponse) response;
}
}

View File

@@ -0,0 +1,321 @@
/*
* Copyright 2004-2010 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.faces.webflow.context.portlet;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import javax.el.ELContext;
import javax.el.ELContextEvent;
import javax.el.ELContextListener;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.VariableMapper;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseStream;
import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* The default FacesContext implementation in Mojarra and in Apache MyFaces depends on the Servlet API. This
* implementation provides an alternative that accepts Portlet request and response structures and creates a
* {@link PortletExternalContextImpl} in its constructor. The rest of the method implementations mimic the equivalent
* methods in the default FacesContext implementation.
*
* @author Rossen Stoyanchev
* @since 2.2.0
*/
public class PortletFacesContextImpl extends FacesContext {
private Application application;
private ELContext elContext;
private ExternalContext externalContext;
private FacesMessage.Severity maximumSeverity;
private List<String> messageClientIds;
private List<FacesMessage> messages;
private boolean released = false;
private RenderKitFactory renderKitFactory;
private boolean renderResponse = false;
private boolean responseComplete = false;
private ResponseStream responseStream;
private ResponseWriter responseWriter;
private UIViewRoot viewRoot;
public PortletFacesContextImpl(PortletContext portletContext, PortletRequest portletRequest,
PortletResponse portletResponse) {
application = ((ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY))
.getApplication();
renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
this.externalContext = new PortletExternalContextImpl(portletContext, portletRequest, portletResponse);
FacesContext.setCurrentInstance(this);
}
public PortletFacesContextImpl(ExternalContext externalContext) {
this.externalContext = externalContext;
}
public ExternalContext getExternalContext() {
assertFacesContextIsNotReleased();
return externalContext;
}
public FacesMessage.Severity getMaximumSeverity() {
assertFacesContextIsNotReleased();
return maximumSeverity;
}
@SuppressWarnings("unchecked")
public Iterator<FacesMessage> getMessages() {
assertFacesContextIsNotReleased();
return (messages != null) ? messages.iterator() : Collections.EMPTY_LIST.iterator();
}
public Application getApplication() {
assertFacesContextIsNotReleased();
return application;
}
public Iterator<String> getClientIdsWithMessages() {
assertFacesContextIsNotReleased();
if (messages == null || messages.isEmpty()) {
return new ArrayList<String>().iterator();
}
return new LinkedHashSet<String>(messageClientIds).iterator();
}
@SuppressWarnings("unchecked")
public Iterator<FacesMessage> getMessages(String clientId) {
assertFacesContextIsNotReleased();
if (messages == null) {
return Collections.EMPTY_LIST.iterator();
}
List<FacesMessage> list = new ArrayList<FacesMessage>();
for (int i = 0; i < messages.size(); i++) {
Object current = messageClientIds.get(i);
if (clientId == null) {
if (current == null) {
list.add(messages.get(i));
}
} else {
if (clientId.equals(current))
list.add(messages.get(i));
}
}
return list.iterator();
}
public RenderKit getRenderKit() {
if (getViewRoot() == null) {
return null;
}
String renderKitId = getViewRoot().getRenderKitId();
if (renderKitId == null) {
return null;
}
return renderKitFactory.getRenderKit(this, renderKitId);
}
public boolean getRenderResponse() {
assertFacesContextIsNotReleased();
return renderResponse;
}
public boolean getResponseComplete() {
assertFacesContextIsNotReleased();
return responseComplete;
}
public ResponseStream getResponseStream() {
assertFacesContextIsNotReleased();
return responseStream;
}
public void setResponseStream(ResponseStream responseStream) {
assertFacesContextIsNotReleased();
if (responseStream == null) {
throw new NullPointerException("responseStream");
}
this.responseStream = responseStream;
}
public ResponseWriter getResponseWriter() {
assertFacesContextIsNotReleased();
return responseWriter;
}
public void setResponseWriter(ResponseWriter responseWriter) {
assertFacesContextIsNotReleased();
if (responseWriter == null) {
throw new NullPointerException("responseWriter");
}
this.responseWriter = responseWriter;
}
public UIViewRoot getViewRoot() {
assertFacesContextIsNotReleased();
return viewRoot;
}
public void setViewRoot(UIViewRoot viewRoot) {
assertFacesContextIsNotReleased();
if (viewRoot == null) {
throw new NullPointerException("viewRoot");
}
this.viewRoot = viewRoot;
}
public void addMessage(String clientId, FacesMessage message) {
assertFacesContextIsNotReleased();
if (message == null) {
throw new NullPointerException("message");
}
if (messages == null) {
messages = new ArrayList<FacesMessage>();
messageClientIds = new ArrayList<String>();
}
messages.add(message);
messageClientIds.add((clientId != null) ? clientId : null);
FacesMessage.Severity severity = message.getSeverity();
if (severity != null) {
if (maximumSeverity == null) {
maximumSeverity = severity;
} else if (severity.compareTo(maximumSeverity) > 0) {
maximumSeverity = severity;
}
}
}
public void release() {
assertFacesContextIsNotReleased();
if (externalContext != null) {
Method delegateMethod = ClassUtils.getMethodIfAvailable(externalContext.getClass(), "release");
if (delegateMethod != null) {
try {
delegateMethod.invoke(externalContext);
} catch (Exception e) {
externalContext.log("Failed to release external context", e);
}
externalContext = null;
}
}
messageClientIds = null;
messages = null;
application = null;
responseStream = null;
responseWriter = null;
viewRoot = null;
released = true;
FacesContext.setCurrentInstance(null);
}
public void renderResponse() {
assertFacesContextIsNotReleased();
renderResponse = true;
}
public void responseComplete() {
assertFacesContextIsNotReleased();
responseComplete = true;
}
public ELContext getELContext() {
if (elContext == null) {
Application application = getApplication();
elContext = new PortletELContextImpl(application.getELResolver());
elContext.putContext(FacesContext.class, FacesContext.getCurrentInstance());
UIViewRoot root = getViewRoot();
if (null != root) {
elContext.setLocale(root.getLocale());
}
ELContextListener[] listeners = application.getELContextListeners();
if (listeners.length > 0) {
ELContextEvent event = new ELContextEvent(elContext);
for (ELContextListener listener : listeners) {
listener.contextCreated(event);
}
}
}
return elContext;
}
private void assertFacesContextIsNotReleased() {
Assert.isTrue(!released, "FacesContext already released");
}
private class PortletELContextImpl extends ELContext {
private FunctionMapper functionMapper;
private VariableMapper variableMapper;
private ELResolver resolver;
public PortletELContextImpl(ELResolver resolver) {
this.resolver = resolver;
}
@Override
public FunctionMapper getFunctionMapper() {
return functionMapper;
}
@Override
public VariableMapper getVariableMapper() {
return variableMapper;
}
@Override
public ELResolver getELResolver() {
return resolver;
}
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2004-2010 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.faces.webflow.context.portlet;
import javax.portlet.PortletRequest;
import org.springframework.webflow.context.portlet.PortletRequestParameterMap;
/**
* Map backed by a PortletContext for accessing Portlet request parameters. Request parameters can have multiple values.
* The {@link RequestParameterMap#setUseArrayForMultiValueAttributes(Boolean)} property allows choosing whether the map
* will return:
* <ul>
* <li>String - selects the first value in case of multiple value parameters</li>
* <li>String[] - wraps single-values parameters as array</li>
* <li>String or String[] - depends on the values of the parameter</li>
* </ul>
*
* @author Rossen Stoyanchev
* @since 2.2.0
*
* @see PortletRequest#getParameter(String)
* @see PortletRequest#getParameterValues(String)
*/
public class RequestParameterMap extends PortletRequestParameterMap {
private Boolean useArrayForMultiValueAttributes;
private PortletRequest portletRequest;
public RequestParameterMap(PortletRequest portletRequest) {
super(portletRequest);
this.portletRequest = portletRequest;
}
public void setUseArrayForMultiValueAttributes(Boolean useArrayForMultiValueAttributes) {
this.useArrayForMultiValueAttributes = useArrayForMultiValueAttributes;
}
/**
* This property allows choosing what kind of attributes the map will return:
* <ol>
* <li>String - selects the first value in case of multiple value parameters</li>
* <li>String[] - wraps single-values parameters as array</li>
* <li>String or String[] - depends on the values of the parameter</li>
* </ol>
* The above choices correspond to the following values for useArrayForMultiValueAttributes:
* <ol>
* <li>False</li>
* <li>True</li>
* <li>null</li>
* </ol>
*
* @param useArrayForMultiValueAttributes
*/
public Boolean useArrayForMultiValueAttributes() {
return useArrayForMultiValueAttributes;
}
@Override
protected Object getAttribute(String key) {
if (null == useArrayForMultiValueAttributes) {
return super.getAttribute(key);
} else {
if (useArrayForMultiValueAttributes) {
return portletRequest.getParameterValues(key);
} else {
return portletRequest.getParameter(key);
}
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2004-2010 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.faces.webflow.context.portlet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.portlet.PortletRequest;
import org.springframework.binding.collection.StringKeyedMapAdapter;
import org.springframework.webflow.core.collection.CollectionUtils;
/**
* Map backed by a PortletContext for accessing Portlet request properties. Request properties can have multiple values.
* The {@link RequestPropertyMap#setUseArrayForMultiValueAttributes(Boolean)} property allows choosing whether the map
* will return:
* <ul>
* <li>String - selects the first element in case of multiple values</li>
* <li>String[] - wraps single-values attributes as array</li>
* <li>String or String[] - depends on the values of the property</li>
* </ul>
*
* @author Rossen Stoyanchev
* @since 2.2.0
*
* @see PortletRequest#getProperty(String)
* @see PortletRequest#getProperties(String)
*/
public class RequestPropertyMap extends StringKeyedMapAdapter {
private Boolean useArrayForMultiValueAttributes;
private final PortletRequest portletRequest;
public RequestPropertyMap(PortletRequest portletRequest) {
this.portletRequest = portletRequest;
}
/**
* This property allows choosing what kind of attributes the map will return:
* <ol>
* <li>String - selects the first element in case of multiple values</li>
* <li>String[] - wraps single-values attributes as array</li>
* <li>String or String[] - depends on the values of the property</li>
* </ol>
* The above choices correspond to the following values for useArrayForMultiValueAttributes:
* <ol>
* <li>False</li>
* <li>True</li>
* <li>null</li>
* </ol>
*
* @param useArrayForMultiValueAttributes
*/
public void setUseArrayForMultiValueAttributes(Boolean useArrayForMultiValueAttributes) {
this.useArrayForMultiValueAttributes = useArrayForMultiValueAttributes;
}
public Boolean useArrayForMultiValueAttributes() {
return useArrayForMultiValueAttributes;
}
@Override
protected Object getAttribute(String key) {
if (null == useArrayForMultiValueAttributes) {
List<String> list = Collections.list(portletRequest.getProperties(key));
if (1 == list.size()) {
return list.get(0);
} else {
return list.toArray(new String[list.size()]);
}
} else {
if (useArrayForMultiValueAttributes) {
List<String> list = Collections.list(portletRequest.getProperties(key));
return list.toArray(new String[list.size()]);
} else {
return portletRequest.getProperty(key);
}
}
}
@Override
protected void setAttribute(String key, Object value) {
throw new UnsupportedOperationException("Cannot set PortletRequest property");
}
@Override
protected void removeAttribute(String key) {
throw new UnsupportedOperationException("Cannot remove PortletRequest property");
}
@Override
@SuppressWarnings("unchecked")
protected Iterator<String> getAttributeNames() {
return CollectionUtils.toIterator(portletRequest.getPropertyNames());
}
}

View File

@@ -16,7 +16,7 @@
<factory>
<application-factory>org.springframework.faces.webflow.FlowApplicationFactory</application-factory>
</factory>
<lifecycle>
<phase-listener>org.springframework.faces.support.RequestLoggingPhaseListener</phase-listener>
</lifecycle>

View File

@@ -0,0 +1,57 @@
package org.springframework.faces.webflow.context.portlet;
import junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockPortletRequest;
public class RequestParameterMapTests extends TestCase {
private RequestParameterMap requestMap;
private MockPortletRequest request;
protected void setUp() throws Exception {
super.setUp();
request = new MockPortletRequest();
requestMap = new RequestParameterMap(request);
}
protected void tearDown() throws Exception {
super.tearDown();
request = null;
requestMap = null;
}
public void testSingleValueParameter() throws Exception {
request.setParameter("key", "value");
assertEquals("value", requestMap.getAttribute("key"));
}
public void testMultiValueParameter() throws Exception {
request.setParameter("key", "value");
request.addParameter("key", "value2");
Object actual = requestMap.getAttribute("key");
assertTrue(actual.getClass().isArray());
assertEquals(2, ((String[]) actual).length);
assertEquals("value", ((String[]) actual)[0]);
assertEquals("value2", ((String[]) actual)[1]);
}
public void testSingleValueParameterAsArray() throws Exception {
request.setParameter("key", "value");
requestMap.setUseArrayForMultiValueAttributes(Boolean.TRUE);
Object actual = requestMap.getAttribute("key");
assertTrue(actual.getClass().isArray());
assertEquals(1, ((String[]) actual).length);
assertEquals("value", ((String[]) actual)[0]);
}
public void testMultiValueParameterAsString() throws Exception {
request.setParameter("key", "value");
request.addParameter("key", "value2");
requestMap.setUseArrayForMultiValueAttributes(Boolean.FALSE);
Object actual = requestMap.getAttribute("key");
assertEquals("value", actual);
}
}

View File

@@ -0,0 +1,56 @@
package org.springframework.faces.webflow.context.portlet;
import junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockPortletRequest;
public class RequestPropertyMapTests extends TestCase {
private RequestPropertyMap requestMap;
private MockPortletRequest request;
protected void setUp() throws Exception {
super.setUp();
request = new MockPortletRequest();
requestMap = new RequestPropertyMap(request);
}
protected void tearDown() throws Exception {
super.tearDown();
request = null;
requestMap = null;
}
public void testSingleValueProperty() throws Exception {
request.setProperty("key", "value");
assertEquals("value", requestMap.getAttribute("key"));
}
public void testMultiValueProperty() throws Exception {
request.setProperty("key", "value");
request.addProperty("key", "value2");
Object actual = requestMap.getAttribute("key");
assertTrue(actual.getClass().isArray());
assertEquals(2, ((String[]) actual).length);
assertEquals("value", ((String[]) actual)[0]);
assertEquals("value2", ((String[]) actual)[1]);
}
public void testSingleValuePropertyAsArray() throws Exception {
request.setProperty("key", "value");
requestMap.setUseArrayForMultiValueAttributes(Boolean.TRUE);
Object actual = requestMap.getAttribute("key");
assertTrue(actual.getClass().isArray());
assertEquals(1, ((String[]) actual).length);
assertEquals("value", ((String[]) actual)[0]);
}
public void testMultiValuePropertyAsString() throws Exception {
request.setProperty("key", "value");
request.addProperty("key", "value2");
requestMap.setUseArrayForMultiValueAttributes(Boolean.FALSE);
Object actual = requestMap.getAttribute("key");
assertEquals("value", actual);
}
}