diff --git a/spring-faces/.classpath b/spring-faces/.classpath index d73b9c86..47086a1b 100644 --- a/spring-faces/.classpath +++ b/spring-faces/.classpath @@ -12,6 +12,7 @@ + diff --git a/spring-faces/ivy.xml b/spring-faces/ivy.xml index 326223d4..1819ae00 100644 --- a/spring-faces/ivy.xml +++ b/spring-faces/ivy.xml @@ -16,7 +16,6 @@ - @@ -29,6 +28,7 @@ + @@ -36,6 +36,7 @@ + @@ -63,10 +64,6 @@ - - - - diff --git a/spring-faces/pom.xml b/spring-faces/pom.xml index 83133272..9a5231d0 100644 --- a/spring-faces/pom.xml +++ b/spring-faces/pom.xml @@ -69,6 +69,12 @@ 1.0 provided + + javax.portlet + portlet-api + 2.0 + provided + javax.servlet servlet-api diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java index 1c8bbd74..d945da6c 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java @@ -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); } diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfRuntimeInformation.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfRuntimeInformation.java index 00915f38..23813c08 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfRuntimeInformation.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfRuntimeInformation.java @@ -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")); + } + } \ No newline at end of file diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/application/portlet/PortletFaceletViewHandler.java b/spring-faces/src/main/java/org/springframework/faces/webflow/application/portlet/PortletFaceletViewHandler.java new file mode 100644 index 00000000..d8e2f232 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/application/portlet/PortletFaceletViewHandler.java @@ -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; + +/** + *

+ * 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. + *

+ * + *

+ * Note: the basis for this code was a Facelets sample provided with Apache MyFaces Portlet Bridge for JSF version + * 2.0.0.alpha-2. + *

+ * + * @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() { + } + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/InitParameterMap.java b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/InitParameterMap.java new file mode 100644 index 00000000..421e44d9 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/InitParameterMap.java @@ -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 getAttributeNames() { + return CollectionUtils.toIterator(portletContext.getInitParameterNames()); + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/PortletExternalContextImpl.java b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/PortletExternalContextImpl.java new file mode 100644 index 00000000..bfd05c4f --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/PortletExternalContextImpl.java @@ -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 applicationMap; + + private boolean isActionRequest; + + private PortletContext portletContext; + + private PortletRequest portletRequest; + + private PortletResponse portletResponse; + + private Map initParameterMap; + + private Map requestHeaderMap; + + private Map requestHeaderValuesMap; + + private Map requestMap; + + private Map requestParameterMap; + + private Map 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 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 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 getRequestCookieMap() { + return Collections.EMPTY_MAP; + } + + @Override + @SuppressWarnings("unchecked") + public Map getRequestHeaderMap() { + if (requestHeaderMap == null) { + RequestPropertyMap map = new RequestPropertyMap(portletRequest); + map.setUseArrayForMultiValueAttributes(Boolean.FALSE); + requestHeaderMap = map; + } + return requestHeaderMap; + } + + @Override + @SuppressWarnings("unchecked") + public Map 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 getRequestLocales() { + return CollectionUtils.toIterator(portletRequest.getLocales()); + } + + @Override + @SuppressWarnings("unchecked") + public Map getRequestMap() { + if (requestMap == null) { + requestMap = new PortletRequestMap(portletRequest); + } + return requestMap; + } + + @Override + @SuppressWarnings("unchecked") + public Map getRequestParameterMap() { + if (requestParameterMap == null) { + RequestParameterMap map = new RequestParameterMap(portletRequest); + map.setUseArrayForMultiValueAttributes(Boolean.FALSE); + requestParameterMap = map; + } + return requestParameterMap; + } + + @Override + @SuppressWarnings("unchecked") + public Iterator getRequestParameterNames() { + return CollectionUtils.toIterator(portletRequest.getParameterNames()); + } + + @Override + @SuppressWarnings("unchecked") + public Map 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 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 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; + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/PortletFacesContextImpl.java b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/PortletFacesContextImpl.java new file mode 100644 index 00000000..279f8c6b --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/PortletFacesContextImpl.java @@ -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 messageClientIds; + + private List 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 getMessages() { + assertFacesContextIsNotReleased(); + return (messages != null) ? messages.iterator() : Collections.EMPTY_LIST.iterator(); + } + + public Application getApplication() { + assertFacesContextIsNotReleased(); + return application; + } + + public Iterator getClientIdsWithMessages() { + assertFacesContextIsNotReleased(); + if (messages == null || messages.isEmpty()) { + return new ArrayList().iterator(); + } + + return new LinkedHashSet(messageClientIds).iterator(); + } + + @SuppressWarnings("unchecked") + public Iterator getMessages(String clientId) { + assertFacesContextIsNotReleased(); + if (messages == null) { + return Collections.EMPTY_LIST.iterator(); + } + + List list = new ArrayList(); + 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(); + messageClientIds = new ArrayList(); + } + 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; + } + + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/RequestParameterMap.java b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/RequestParameterMap.java new file mode 100644 index 00000000..e8f26f06 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/RequestParameterMap.java @@ -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: + *
    + *
  • String - selects the first value in case of multiple value parameters
  • + *
  • String[] - wraps single-values parameters as array
  • + *
  • String or String[] - depends on the values of the parameter
  • + *
+ * + * @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: + *
    + *
  1. String - selects the first value in case of multiple value parameters
  2. + *
  3. String[] - wraps single-values parameters as array
  4. + *
  5. String or String[] - depends on the values of the parameter
  6. + *
+ * The above choices correspond to the following values for useArrayForMultiValueAttributes: + *
    + *
  1. False
  2. + *
  3. True
  4. + *
  5. null
  6. + *
+ * + * @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); + } + } + } + +} \ No newline at end of file diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/RequestPropertyMap.java b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/RequestPropertyMap.java new file mode 100644 index 00000000..b8e9ac67 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/context/portlet/RequestPropertyMap.java @@ -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: + *
    + *
  • String - selects the first element in case of multiple values
  • + *
  • String[] - wraps single-values attributes as array
  • + *
  • String or String[] - depends on the values of the property
  • + *
+ * + * @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: + *
    + *
  1. String - selects the first element in case of multiple values
  2. + *
  3. String[] - wraps single-values attributes as array
  4. + *
  5. String or String[] - depends on the values of the property
  6. + *
+ * The above choices correspond to the following values for useArrayForMultiValueAttributes: + *
    + *
  1. False
  2. + *
  3. True
  4. + *
  5. null
  6. + *
+ * + * @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 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 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 getAttributeNames() { + return CollectionUtils.toIterator(portletRequest.getPropertyNames()); + } + +} diff --git a/spring-faces/src/main/resources/META-INF/faces-config.xml b/spring-faces/src/main/resources/META-INF/faces-config.xml index c5a1cb66..b379dd9d 100644 --- a/spring-faces/src/main/resources/META-INF/faces-config.xml +++ b/spring-faces/src/main/resources/META-INF/faces-config.xml @@ -16,7 +16,7 @@ org.springframework.faces.webflow.FlowApplicationFactory - + org.springframework.faces.support.RequestLoggingPhaseListener diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/context/portlet/RequestParameterMapTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/context/portlet/RequestParameterMapTests.java new file mode 100644 index 00000000..766d823e --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/webflow/context/portlet/RequestParameterMapTests.java @@ -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); + } + +} diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/context/portlet/RequestPropertyMapTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/context/portlet/RequestPropertyMapTests.java new file mode 100644 index 00000000..b36c312a --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/webflow/context/portlet/RequestPropertyMapTests.java @@ -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); + } +} diff --git a/spring-faces/template.mf b/spring-faces/template.mf index 7edf0978..e3f435c7 100644 --- a/spring-faces/template.mf +++ b/spring-faces/template.mf @@ -11,6 +11,7 @@ Import-Template: org.springframework.*;version="[3.0.4.RELEASE, 3.1.0)", org.apache.commons.logging;version="[1.1.1, 2.0.0)", javax.el;version="[1.0.0, 2.0.0)", + javax.portlet;version="[2.0.0, 3.0.0)";resolution:=optional, javax.servlet;version="[2.4.0, 3.0.0)", javax.servlet.http;version="[2.4.0, 3.0.0)", javax.faces.*;version="[1.2.0, 3.0.0)", diff --git a/spring-webflow-samples/booking-portlet-faces/.classpath b/spring-webflow-samples/booking-portlet-faces/.classpath index 984143e6..eaec100b 100644 --- a/spring-webflow-samples/booking-portlet-faces/.classpath +++ b/spring-webflow-samples/booking-portlet-faces/.classpath @@ -8,5 +8,6 @@ + diff --git a/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.core.prefs b/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.core.prefs index 94a557d8..1b9c1162 100644 --- a/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.core.prefs +++ b/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,4 @@ -#Wed Jun 02 12:02:05 BST 2010 +#Sun Sep 12 18:04:00 GMT 2010 eclipse.preferences.version=1 instance/org.eclipse.core.net/org.eclipse.core.net.hasMigrated=true org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled @@ -56,13 +56,13 @@ org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false -org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=false org.eclipse.jdt.core.formatter.comment.format_header=false -org.eclipse.jdt.core.formatter.comment.format_html=true -org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true -org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_html=false +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=false +org.eclipse.jdt.core.formatter.comment.format_line_comments=false org.eclipse.jdt.core.formatter.comment.format_source_code=true -org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false org.eclipse.jdt.core.formatter.comment.indent_root_tags=false org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert @@ -83,6 +83,9 @@ org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false org.eclipse.jdt.core.formatter.indentation.size=4 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert @@ -251,6 +254,8 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false @@ -261,7 +266,7 @@ org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true -org.eclipse.jdt.core.formatter.tabulation.char=mixed -org.eclipse.jdt.core.formatter.tabulation.size=8 +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true diff --git a/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.ui.prefs b/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.ui.prefs index e0d5724c..4f2e332f 100644 --- a/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.ui.prefs +++ b/spring-webflow-samples/booking-portlet-faces/.settings/org.eclipse.jdt.ui.prefs @@ -1,7 +1,7 @@ -#Thu Aug 09 13:12:30 EDT 2007 +#Sun Sep 12 18:04:00 GMT 2010 eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true -formatter_profile=_Spring Java Conventions +formatter_profile=_Spring formatter_settings_version=11 instance/org.eclipse.core.net/org.eclipse.core.net.hasMigrated=true sp_cleanup.add_default_serial_version_id=true diff --git a/spring-webflow-samples/booking-portlet-faces/ivy.xml b/spring-webflow-samples/booking-portlet-faces/ivy.xml index ba46e2a0..5abf4b36 100644 --- a/spring-webflow-samples/booking-portlet-faces/ivy.xml +++ b/spring-webflow-samples/booking-portlet-faces/ivy.xml @@ -47,8 +47,11 @@ - + + + + diff --git a/spring-webflow-samples/booking-portlet-faces/src/main/java/org/springframework/webflow/samples/PortletFaceletViewHandler.java b/spring-webflow-samples/booking-portlet-faces/src/main/java/org/springframework/webflow/samples/PortletFaceletViewHandler.java deleted file mode 100644 index 821e710d..00000000 --- a/spring-webflow-samples/booking-portlet-faces/src/main/java/org/springframework/webflow/samples/PortletFaceletViewHandler.java +++ /dev/null @@ -1,172 +0,0 @@ -/** - * 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.samples; - -import java.io.IOException; -import java.io.Writer; -import java.util.Map; -import java.util.logging.Level; - -import javax.faces.FacesException; -import javax.faces.application.ViewHandler; -import javax.faces.context.ExternalContext; -import javax.faces.context.FacesContext; -import javax.faces.context.ResponseWriter; -import javax.faces.render.RenderKit; -import javax.portlet.MimeResponse; -import javax.portlet.faces.Bridge; - -import com.sun.facelets.FaceletViewHandler; - -/** - * See: http://www.mail-archive.com/users@myfaces.apache.org/msg55213.html - */ -public class PortletFaceletViewHandler extends FaceletViewHandler { - - public PortletFaceletViewHandler(ViewHandler parent) { - super(new FaceletViewHandler(parent)); - } - - protected ResponseWriter createResponseWriter(FacesContext context) throws IOException, FacesException { - // Only override if in a portlet request - if (context.getExternalContext().getRequestMap().get(Bridge.PORTLET_LIFECYCLE_PHASE) == null) { - return super.createResponseWriter(context); - } - - ExternalContext extContext = context.getExternalContext(); - RenderKit renderKit = context.getRenderKit(); - // Avoid a cryptic NullPointerException when the renderkit ID - // is incorrectly set - if (renderKit == null) { - String id = context.getViewRoot().getRenderKitId(); - throw new IllegalStateException("No render kit was available for id \"" + id + "\""); - } - - MimeResponse response = (MimeResponse) extContext.getResponse(); - - // get our content type - String contentType = (String) extContext.getRequestMap().get("facelets.ContentType"); - - // get the encoding - String encoding = (String) extContext.getRequestMap().get("facelets.Encoding"); - - ResponseWriter writer; - // append */* to the contentType so createResponseWriter will succeed no matter - // the requested contentType. - if (contentType != null && !contentType.equals("*/*")) { - contentType += ",*/*"; - } - // Create a dummy ResponseWriter with a bogus writer, - // so we can figure out what content type the ReponseWriter - // is really going to ask for - try { - writer = renderKit.createResponseWriter(NullWriter.Instance, contentType, encoding); - } catch (IllegalArgumentException e) { - // Added because of an RI bug prior to 1.2_05-b3. Might as well leave it in case other impls have the same - // problem. - // https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=613 - log.fine("The impl didn't correctly handled '*/*' in the content type list. Trying '*/*' directly."); - writer = renderKit.createResponseWriter(NullWriter.Instance, "*/*", encoding); - } - - // Override the JSF provided content type if necessary - contentType = getResponseContentType(context, writer.getContentType()); - encoding = getResponseEncoding(context, writer.getCharacterEncoding()); - - // apply them to the response - response.setContentType(contentType + "; charset=" + encoding); - - // removed 2005.8.23 to comply with J2EE 1.3 - // response.setCharacterEncoding(encoding); - - // Now, clone with the real writer - writer = writer.cloneWithWriter(response.getWriter()); - - return writer; - } - - /** - * Generate the encoding - * - * @param context - * @param orig - * @return - */ - protected String getResponseEncoding(FacesContext context, String orig) { - String encoding = orig; - - // see if we need to override the encoding - Map m = context.getExternalContext().getRequestMap(); - Map sm = context.getExternalContext().getSessionMap(); - - // 1. check the request attribute - if (m.containsKey("facelets.Encoding")) { - encoding = (String) m.get("facelets.Encoding"); - if (log.isLoggable(Level.FINEST)) { - log.finest("Facelet specified alternate encoding '" + encoding + "'"); - } - sm.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) sm.get(CHARACTER_ENCODING_KEY); - if (log.isLoggable(Level.FINEST)) { - log.finest("Session specified alternate encoding '" + encoding + "'"); - } - } - - // 4. default it - if (encoding == null) { - encoding = "UTF-8"; - if (log.isLoggable(Level.FINEST)) { - log.finest("ResponseWriter created had a null CharacterEncoding, defaulting to UTF-8"); - } - } - - return encoding; - } - - protected static class NullWriter extends Writer { - - static final NullWriter Instance = new NullWriter(); - - 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() { - } - } - -} \ No newline at end of file diff --git a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/faces-config.xml b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/faces-config.xml index b06693cc..67a0dac8 100644 --- a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/faces-config.xml +++ b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/faces-config.xml @@ -5,7 +5,6 @@ - - org.springframework.webflow.samples.PortletFaceletViewHandler + org.springframework.faces.webflow.application.portlet.PortletFaceletViewHandler - \ No newline at end of file + diff --git a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/booking/enterBookingDetails.xhtml b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/booking/enterBookingDetails.xhtml index 620d9c75..982cda94 100644 --- a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/booking/enterBookingDetails.xhtml +++ b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/booking/enterBookingDetails.xhtml @@ -2,8 +2,7 @@ + xmlns:f="http://java.sun.com/jsf/core"> @@ -45,11 +44,9 @@ Check In Date: - - - - - + + + @@ -57,11 +54,9 @@ Check Out Date: - - - - - + + + @@ -89,9 +84,7 @@ Credit Card #: - - - + @@ -99,9 +92,7 @@ Credit Card Name: - - - + @@ -119,9 +110,7 @@ - -   - +   diff --git a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.xhtml b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.xhtml index c850479b..9b559b52 100644 --- a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.xhtml +++ b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.xhtml @@ -2,82 +2,76 @@ + xmlns:f="http://java.sun.com/jsf/core"> - -
-

Search Hotels

- - - - -
-
-
- - - -
-
- Maximum results: - - - -
-
- -
+
+

Search Hotels

+ + + + +
+
+
+ Search String: +
-
-
-
- +
+ Maximum results: + + + +
+
+ +
+
+
+
+
- -
- -

Current Hotel Bookings

- - - - Name - #{booking.hotel.name} - - - Address - #{booking.hotel.address} - - - City, State - #{booking.hotel.city}, #{booking.hotel.state} - - - Check in date - - - - - - Check out date - - - - - - Confirmation number - #{booking.id} - - - Action - - - -
-
-
+
+ +

Current Hotel Bookings

+ + + + Name + #{booking.hotel.name} + + + Address + #{booking.hotel.address} + + + City, State + #{booking.hotel.city}, #{booking.hotel.state} + + + Check in date + + + + + + Check out date + + + + + + Confirmation number + #{booking.id} + + + Action + + + +
+
\ No newline at end of file diff --git a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/main.xml b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/main.xml index 8cbeb06c..2c798207 100644 --- a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/main.xml +++ b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/main.xml @@ -12,7 +12,6 @@ - @@ -22,14 +21,12 @@ - - - + @@ -46,15 +43,6 @@ - - - - - - - - - \ No newline at end of file diff --git a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotel.xhtml b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotel.xhtml index 50b6fd58..9517b4a3 100644 --- a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotel.xhtml +++ b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotel.xhtml @@ -2,8 +2,7 @@ + xmlns:f="http://java.sun.com/jsf/core"> @@ -45,10 +44,8 @@ -
-   - -
+   +
diff --git a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotels.xhtml b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotels.xhtml index 2f040099..ad9d944d 100644 --- a/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotels.xhtml +++ b/spring-webflow-samples/booking-portlet-faces/src/main/webapp/WEB-INF/flows/main/reviewHotels.xhtml @@ -2,49 +2,46 @@ + xmlns:f="http://java.sun.com/jsf/core">

Hotel Results

- +

- -
- - - - Name - #{hotel.name} - - - Address - #{hotel.address} - - - City, State - #{hotel.city}, #{hotel.state}, #{hotel.country} - - - Zip - #{hotel.zip} - - - Action - - - - - +
+ + + + Name + #{hotel.name} + + + Address + #{hotel.address} + + + City, State + #{hotel.city}, #{hotel.state}, #{hotel.country} + + + Zip + #{hotel.zip} + + + Action + + + + - + +
diff --git a/spring-webflow-samples/booking-portlet-mvc/src/main/java/org/springframework/webflow/samples/booking/SearchCriteria.java b/spring-webflow-samples/booking-portlet-mvc/src/main/java/org/springframework/webflow/samples/booking/SearchCriteria.java index 0db814b3..ba0b2f01 100644 --- a/spring-webflow-samples/booking-portlet-mvc/src/main/java/org/springframework/webflow/samples/booking/SearchCriteria.java +++ b/spring-webflow-samples/booking-portlet-mvc/src/main/java/org/springframework/webflow/samples/booking/SearchCriteria.java @@ -2,7 +2,6 @@ package org.springframework.webflow.samples.booking; import java.io.Serializable; - /** * A backing bean for the main hotel search form. Encapsulates the criteria needed to perform a hotel search. * @@ -53,4 +52,17 @@ public class SearchCriteria implements Serializable { public void setPage(int page) { this.page = page; } + + public void nextPage() { + page++; + } + + public void previousPage() { + page--; + } + + public void resetPage() { + page = 0; + } + } \ No newline at end of file diff --git a/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.jsp b/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.jsp index 10620e27..84bc87a2 100644 --- a/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.jsp +++ b/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/enterSearchCriteria.jsp @@ -11,6 +11,7 @@
+ diff --git a/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/main.xml b/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/main.xml index 714b7ccf..fbd0607c 100644 --- a/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/main.xml +++ b/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/main.xml @@ -9,7 +9,9 @@ - + + + diff --git a/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/reviewHotels.jsp b/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/reviewHotels.jsp index ee66c034..b760e99c 100644 --- a/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/reviewHotels.jsp +++ b/spring-webflow-samples/booking-portlet-mvc/src/main/webapp/WEB-INF/flows/main/reviewHotels.jsp @@ -1,9 +1,16 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

Hotel Results

+ + + + +Change Search + @@ -38,3 +45,19 @@
+ + + + + + + Previous Results + + + + + + + More Results + + diff --git a/spring-webflow/pom.xml b/spring-webflow/pom.xml index 5d2bf6a9..6351afcc 100644 --- a/spring-webflow/pom.xml +++ b/spring-webflow/pom.xml @@ -73,7 +73,7 @@ javax.portlet portlet-api - 1.0 + 2.0 provided diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java index 598640ab..27586b5c 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java @@ -232,7 +232,9 @@ public class ViewState extends TransitionableState { context.getFlashScope().put(View.USER_EVENT_STATE_ATTRIBUTE, view.getUserEventState()); externalContext.requestFlowExecutionRedirect(); } else { - render(context, view); + if (externalContext.isResponseAllowed()) { + render(context, view); + } } } }