SWF-1367 Add support for JSF 2 Ajax requests, update booking-faces sample (JSF 2 based Ajax, jsr-303 validation), add chained delegation to AjaxHandlers, other miscellaneous improvements
This commit is contained in:
@@ -17,47 +17,39 @@ package org.springframework.faces.richfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.FactoryFinder;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.FacesContextFactory;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.ajax4jsf.context.AjaxContext;
|
||||
import org.springframework.faces.webflow.FlowLifecycle;
|
||||
import org.springframework.faces.webflow.FacesContextHelper;
|
||||
import org.springframework.js.ajax.AbstractAjaxHandler;
|
||||
import org.springframework.js.ajax.AjaxHandler;
|
||||
import org.springframework.js.ajax.SpringJavascriptAjaxHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.support.WebApplicationObjectSupport;
|
||||
|
||||
/**
|
||||
* Ajax handler that works with Rich Faces, allowing support for Web Flow Ajax features with the Rich Faces toolkit.
|
||||
*
|
||||
* @see AbstractAjaxHandler
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
*/
|
||||
public class RichFacesAjaxHandler extends WebApplicationObjectSupport implements AjaxHandler {
|
||||
public class RichFacesAjaxHandler extends AbstractAjaxHandler implements AjaxHandler {
|
||||
|
||||
private AjaxHandler delegate = new SpringJavascriptAjaxHandler();
|
||||
|
||||
public boolean isAjaxRequest(HttpServletRequest request, HttpServletResponse response) {
|
||||
if (isRichFacesAjaxRequest(request, response)) {
|
||||
return true;
|
||||
} else {
|
||||
return delegate.isAjaxRequest(request, response);
|
||||
}
|
||||
/**
|
||||
* Create a RichFacesAjaxHandler that is not part of a chain of AjaxHandler's.
|
||||
*/
|
||||
public RichFacesAjaxHandler() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public void sendAjaxRedirect(String targetUrl, HttpServletRequest request, HttpServletResponse response,
|
||||
boolean popup) throws IOException {
|
||||
if (isRichFacesAjaxRequest(request, response)) {
|
||||
response.sendRedirect(response.encodeRedirectURL(targetUrl));
|
||||
} else {
|
||||
delegate.sendAjaxRedirect(targetUrl, request, response, popup);
|
||||
}
|
||||
/**
|
||||
* Create a RichFacesAjaxHandler as part of a chain of AjaxHandler's.
|
||||
*/
|
||||
public RichFacesAjaxHandler(AbstractAjaxHandler delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
protected boolean isRichFacesAjaxRequest(HttpServletRequest request, HttpServletResponse response) {
|
||||
protected boolean isAjaxRequestInternal(HttpServletRequest request, HttpServletResponse response) {
|
||||
FacesContextHelper helper = new FacesContextHelper();
|
||||
try {
|
||||
FacesContext facesContext = helper.getFacesContext(getServletContext(), request, response);
|
||||
@@ -68,33 +60,13 @@ public class RichFacesAjaxHandler extends WebApplicationObjectSupport implements
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
helper.cleanup();
|
||||
helper.releaseIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
private static class FacesContextHelper {
|
||||
|
||||
private boolean created = false;
|
||||
|
||||
protected FacesContext getFacesContext(ServletContext context, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
if (FacesContext.getCurrentInstance() != null) {
|
||||
return FacesContext.getCurrentInstance();
|
||||
} else {
|
||||
FacesContextFactory facesContextFactory = (FacesContextFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
|
||||
FacesContext defaultFacesContext = facesContextFactory.getFacesContext(context, request, response,
|
||||
FlowLifecycle.newInstance());
|
||||
Assert.notNull(defaultFacesContext, "Creation of the default FacesContext failed.");
|
||||
created = true;
|
||||
return defaultFacesContext;
|
||||
}
|
||||
}
|
||||
|
||||
protected void cleanup() {
|
||||
if (created) {
|
||||
FacesContext.getCurrentInstance().release();
|
||||
}
|
||||
}
|
||||
protected void sendAjaxRedirectInternal(String targetUrl, HttpServletRequest request, HttpServletResponse response,
|
||||
boolean popup) throws IOException {
|
||||
response.sendRedirect(response.encodeRedirectURL(targetUrl));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* 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.ui;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIViewRoot;
|
||||
import javax.faces.component.behavior.ClientBehavior;
|
||||
import javax.faces.component.visit.VisitCallback;
|
||||
import javax.faces.component.visit.VisitContext;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ComponentSystemEvent;
|
||||
import javax.faces.event.ComponentSystemEventListener;
|
||||
import javax.faces.event.PhaseId;
|
||||
import javax.faces.event.SystemEventListener;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A subclass of AjaxViewRoot for use with JSF 2.0.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Note that these methods should ideally be in DelegatingViewRoot.java. However, due to some introspection done by the
|
||||
* JSF runtime that would make it impossible to use in JSF 1.2 because some of the delegate methods contain JSF 2.0
|
||||
* specific types and cause ClassNotFoundExceptions.
|
||||
* </p>
|
||||
*
|
||||
* @author Phil Webb
|
||||
*/
|
||||
public class Jsf2AjaxViewRoot extends AjaxViewRoot {
|
||||
|
||||
public Jsf2AjaxViewRoot(UIViewRoot original) {
|
||||
super(original);
|
||||
}
|
||||
|
||||
public void addClientBehavior(String eventName, ClientBehavior behavior) {
|
||||
getOriginalViewRoot().addClientBehavior(eventName, behavior);
|
||||
}
|
||||
|
||||
public void addComponentResource(FacesContext context, UIComponent componentResource) {
|
||||
getOriginalViewRoot().addComponentResource(context, componentResource);
|
||||
}
|
||||
|
||||
public void addComponentResource(FacesContext context, UIComponent componentResource, String target) {
|
||||
getOriginalViewRoot().addComponentResource(context, componentResource, target);
|
||||
}
|
||||
|
||||
public void broadcastEvents(FacesContext context, PhaseId phaseId) {
|
||||
getOriginalViewRoot().broadcastEvents(context, phaseId);
|
||||
}
|
||||
|
||||
public void clearInitialState() {
|
||||
getOriginalViewRoot().clearInitialState();
|
||||
}
|
||||
|
||||
public String createUniqueId(FacesContext context, String seed) {
|
||||
return getOriginalViewRoot().createUniqueId(context, seed);
|
||||
}
|
||||
|
||||
public Map getClientBehaviors() {
|
||||
return getOriginalViewRoot().getClientBehaviors();
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return getOriginalViewRoot().getClientId();
|
||||
}
|
||||
|
||||
public List getComponentResources(FacesContext context, String target) {
|
||||
return getOriginalViewRoot().getComponentResources(context, target);
|
||||
}
|
||||
|
||||
public String getDefaultEventName() {
|
||||
return getOriginalViewRoot().getDefaultEventName();
|
||||
}
|
||||
|
||||
public Collection getEventNames() {
|
||||
return getOriginalViewRoot().getEventNames();
|
||||
}
|
||||
|
||||
public List getListenersForEventClass(Class eventClass) {
|
||||
return getOriginalViewRoot().getListenersForEventClass(eventClass);
|
||||
}
|
||||
|
||||
public UIComponent getNamingContainer() {
|
||||
return getOriginalViewRoot().getNamingContainer();
|
||||
}
|
||||
|
||||
public List getPhaseListeners() {
|
||||
return getOriginalViewRoot().getPhaseListeners();
|
||||
}
|
||||
|
||||
public Map getResourceBundleMap() {
|
||||
return getOriginalViewRoot().getResourceBundleMap();
|
||||
}
|
||||
|
||||
public List getViewListenersForEventClass(Class systemEvent) {
|
||||
return getOriginalViewRoot().getViewListenersForEventClass(systemEvent);
|
||||
}
|
||||
|
||||
public Map getViewMap() {
|
||||
return getOriginalViewRoot().getViewMap();
|
||||
}
|
||||
|
||||
public Map getViewMap(boolean create) {
|
||||
return getOriginalViewRoot().getViewMap(create);
|
||||
}
|
||||
|
||||
public boolean initialStateMarked() {
|
||||
return getOriginalViewRoot().initialStateMarked();
|
||||
}
|
||||
|
||||
public void markInitialState() {
|
||||
getOriginalViewRoot().markInitialState();
|
||||
}
|
||||
|
||||
public boolean isInView() {
|
||||
return getOriginalViewRoot().isInView();
|
||||
}
|
||||
|
||||
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
|
||||
getOriginalViewRoot().processEvent(event);
|
||||
}
|
||||
|
||||
public void removeComponentResource(FacesContext context, UIComponent componentResource) {
|
||||
getOriginalViewRoot().removeComponentResource(context, componentResource);
|
||||
}
|
||||
|
||||
public void removeComponentResource(FacesContext context, UIComponent componentResource, String target) {
|
||||
getOriginalViewRoot().removeComponentResource(context, componentResource, target);
|
||||
}
|
||||
|
||||
public void setInView(boolean isInView) {
|
||||
getOriginalViewRoot().setInView(isInView);
|
||||
}
|
||||
|
||||
public void subscribeToEvent(Class eventClass, ComponentSystemEventListener componentListener) {
|
||||
getOriginalViewRoot().subscribeToEvent(eventClass, componentListener);
|
||||
}
|
||||
|
||||
public void subscribeToViewEvent(Class systemEvent, SystemEventListener listener) {
|
||||
getOriginalViewRoot().subscribeToViewEvent(systemEvent, listener);
|
||||
}
|
||||
|
||||
public void unsubscribeFromEvent(Class eventClass, ComponentSystemEventListener componentListener) {
|
||||
getOriginalViewRoot().unsubscribeFromEvent(eventClass, componentListener);
|
||||
}
|
||||
|
||||
public void unsubscribeFromViewEvent(Class systemEvent, SystemEventListener listener) {
|
||||
getOriginalViewRoot().unsubscribeFromViewEvent(systemEvent, listener);
|
||||
}
|
||||
|
||||
public boolean visitTree(VisitContext context, VisitCallback callback) {
|
||||
return getOriginalViewRoot().visitTree(context, callback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import javax.faces.FactoryFinder;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.FacesContextFactory;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Provides helper methods for getting a FacesContext that is suitable for use outside of Web Flow. Inside a running
|
||||
* Flow session {@link FlowFacesContext} is typically used instead.
|
||||
* <p>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class FacesContextHelper {
|
||||
|
||||
private boolean release = false;
|
||||
|
||||
public FacesContext getFacesContext(ServletContext servletContext, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
FacesContext facesContext = null;
|
||||
if (FacesContext.getCurrentInstance() != null) {
|
||||
facesContext = FacesContext.getCurrentInstance();
|
||||
} else {
|
||||
FacesContextFactory factory = (FacesContextFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
|
||||
facesContext = factory.getFacesContext(servletContext, request, response, FlowLifecycle.newInstance());
|
||||
release = true;
|
||||
}
|
||||
return facesContext;
|
||||
}
|
||||
|
||||
public void releaseIfNecessary() {
|
||||
if (release) {
|
||||
FacesContext.getCurrentInstance().release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,12 +17,8 @@ package org.springframework.faces.webflow;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.FactoryFinder;
|
||||
import javax.faces.application.ResourceHandler;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.FacesContextFactory;
|
||||
import javax.faces.lifecycle.Lifecycle;
|
||||
import javax.faces.lifecycle.LifecycleFactory;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -42,18 +38,14 @@ public class FacesJsfResourceRequestHandler extends WebApplicationObjectSupport
|
||||
|
||||
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException,
|
||||
IOException {
|
||||
FacesContext context = getFacesContext(request, response);
|
||||
ResourceHandler resourceHandler = context.getApplication().getResourceHandler();
|
||||
resourceHandler.handleResourceRequest(context);
|
||||
}
|
||||
|
||||
private FacesContext getFacesContext(HttpServletRequest request, HttpServletResponse response) {
|
||||
FacesContextFactory facesContextFactory = (FacesContextFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
|
||||
LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
|
||||
Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
|
||||
return facesContextFactory.getFacesContext(getServletContext(), request, response, lifecycle);
|
||||
FacesContextHelper helper = new FacesContextHelper();
|
||||
try {
|
||||
FacesContext facesContext = helper.getFacesContext(getServletContext(), request, response);
|
||||
ResourceHandler resourceHandler = facesContext.getApplication().getResourceHandler();
|
||||
resourceHandler.handleResourceRequest(facesContext);
|
||||
} finally {
|
||||
helper.releaseIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,16 +81,16 @@ public class FlowFacesContext extends FacesContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a FacesMessage to an SWF Message and adds it to the current MessageContext
|
||||
* Translates a FacesMessage to a Spring Web Flow message and adds it to the current MessageContext
|
||||
*/
|
||||
public void addMessage(String clientId, FacesMessage message) {
|
||||
messageDelegate.addMessage(clientId, message);
|
||||
messageDelegate.addToFlowMessageContext(clientId, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Iterator for all component clientId's for which messages have been added.
|
||||
*/
|
||||
public Iterator getClientIdsWithMessages() {
|
||||
public Iterator<String> getClientIdsWithMessages() {
|
||||
return messageDelegate.getClientIdsWithMessages();
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public class FlowFacesContext extends FacesContext {
|
||||
/**
|
||||
* Returns an Iterator for all Messages in the current MessageContext that does translation to FacesMessages.
|
||||
*/
|
||||
public Iterator getMessages() {
|
||||
public Iterator<FacesMessage> getMessages() {
|
||||
return messageDelegate.getMessages();
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public class FlowFacesContext extends FacesContext {
|
||||
* Returns an Iterator for all Messages with the given clientId in the current MessageContext that does translation
|
||||
* to FacesMessages.
|
||||
*/
|
||||
public Iterator getMessages(String clientId) {
|
||||
public Iterator<FacesMessage> getMessages(String clientId) {
|
||||
return messageDelegate.getMessages(clientId);
|
||||
}
|
||||
|
||||
@@ -200,6 +200,10 @@ public class FlowFacesContext extends FacesContext {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
protected FlowFacesContextMessageDelegate getMessageDelegate() {
|
||||
return messageDelegate;
|
||||
}
|
||||
|
||||
protected class FlowExternalContext extends ExternalContextWrapper {
|
||||
|
||||
private static final String CUSTOM_RESPONSE = "customResponse";
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -63,7 +64,7 @@ public class FlowFacesContextMessageDelegate {
|
||||
/**
|
||||
* @see FlowFacesContext#addMessage(String, FacesMessage)
|
||||
*/
|
||||
public void addMessage(String clientId, FacesMessage message) {
|
||||
public void addToFlowMessageContext(String clientId, FacesMessage message) {
|
||||
String source = null;
|
||||
if (StringUtils.hasText(clientId)) {
|
||||
source = clientId;
|
||||
@@ -75,7 +76,7 @@ public class FlowFacesContextMessageDelegate {
|
||||
/**
|
||||
* @see FlowFacesContext#getClientIdsWithMessages
|
||||
*/
|
||||
public Iterator getClientIdsWithMessages() {
|
||||
public Iterator<String> getClientIdsWithMessages() {
|
||||
return new ClientIdIterator();
|
||||
}
|
||||
|
||||
@@ -102,19 +103,61 @@ public class FlowFacesContextMessageDelegate {
|
||||
/**
|
||||
* @see FlowFacesContext#getMessages()
|
||||
*/
|
||||
public Iterator getMessages() {
|
||||
public Iterator<FacesMessage> getMessages() {
|
||||
return new FacesMessageIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FlowFacesContext#getMessages(String)
|
||||
*/
|
||||
public Iterator getMessages(String clientId) {
|
||||
public Iterator<FacesMessage> getMessages(String clientId) {
|
||||
return new FacesMessageIterator(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Jsf2FlowFacesContext#getMessageList()
|
||||
*/
|
||||
public List<FacesMessage> getMessageList() {
|
||||
return getMessageList(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Jsf2FlowFacesContext#getMessageList(String)
|
||||
*/
|
||||
public List<FacesMessage> getMessageList(String clientId) {
|
||||
List<FacesMessage> messages = getFacesMessages(clientId);
|
||||
if (null == messages) {
|
||||
return Collections.unmodifiableList(Collections.<FacesMessage> emptyList());
|
||||
} else {
|
||||
return Collections.unmodifiableList(messages);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------ Private helper methods ----------------------//
|
||||
|
||||
private List<FacesMessage> getFacesMessages() {
|
||||
return getFacesMessages(null);
|
||||
}
|
||||
|
||||
private List<FacesMessage> getFacesMessages(String clientId) {
|
||||
List<FacesMessage> translatedMessages = new ArrayList<FacesMessage>();
|
||||
|
||||
Message[] summaryMessages = context.getMessageContext().getMessagesByCriteria(
|
||||
new MatchBySuffixCriteria(clientId, SUMMARY_MESSAGE_KEY));
|
||||
Message[] detailMessages = context.getMessageContext().getMessagesByCriteria(
|
||||
new MatchBySuffixCriteria(clientId, DETAIL_MESSAGE_KEY));
|
||||
for (int i = 0; i < summaryMessages.length; i++) {
|
||||
translatedMessages.add(toFacesMessage(summaryMessages[i], detailMessages[i]));
|
||||
}
|
||||
|
||||
Message[] userMessages = context.getMessageContext().getMessagesByCriteria(new UserMessageCriteria(clientId));
|
||||
for (int z = 0; z < userMessages.length; z++) {
|
||||
translatedMessages.add(toFacesMessage(userMessages[z], userMessages[z]));
|
||||
}
|
||||
|
||||
return translatedMessages;
|
||||
}
|
||||
|
||||
private FacesMessage toFacesMessage(Message summaryMessage, Message detailMessage) {
|
||||
|
||||
// If we can return the actual message instance.
|
||||
@@ -137,49 +180,25 @@ public class FlowFacesContextMessageDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private class FacesMessageIterator implements Iterator {
|
||||
private class FacesMessageIterator implements Iterator<FacesMessage> {
|
||||
|
||||
private Object[] messages;
|
||||
private FacesMessage[] messages;
|
||||
|
||||
private int currentIndex = -1;
|
||||
|
||||
protected FacesMessageIterator() {
|
||||
Message[] summaryMessages = context.getMessageContext().getMessagesByCriteria(new SummaryMessageCriteria());
|
||||
Message[] detailMessages = context.getMessageContext().getMessagesByCriteria(new DetailMessageCriteria());
|
||||
Message[] userMessages = context.getMessageContext().getMessagesByCriteria(new UserMessageCriteria());
|
||||
|
||||
List translatedMessages = new ArrayList();
|
||||
for (int i = 0; i < summaryMessages.length; i++) {
|
||||
translatedMessages.add(toFacesMessage(summaryMessages[i], detailMessages[i]));
|
||||
}
|
||||
for (int z = 0; z < userMessages.length; z++) {
|
||||
translatedMessages.add(toFacesMessage(userMessages[z], userMessages[z]));
|
||||
}
|
||||
|
||||
this.messages = translatedMessages.toArray();
|
||||
this.messages = getFacesMessages().toArray(new FacesMessage[] {});
|
||||
}
|
||||
|
||||
protected FacesMessageIterator(String clientId) {
|
||||
Message[] summaryMessages = context.getMessageContext().getMessagesBySource(clientId + SUMMARY_MESSAGE_KEY);
|
||||
Message[] detailMessages = context.getMessageContext().getMessagesBySource(clientId + DETAIL_MESSAGE_KEY);
|
||||
Message[] userMessages = context.getMessageContext().getMessagesBySource(clientId);
|
||||
|
||||
List translatedMessages = new ArrayList();
|
||||
for (int i = 0; i < summaryMessages.length; i++) {
|
||||
translatedMessages.add(toFacesMessage(summaryMessages[i], detailMessages[i]));
|
||||
}
|
||||
for (int z = 0; z < userMessages.length; z++) {
|
||||
translatedMessages.add(toFacesMessage(userMessages[z], userMessages[z]));
|
||||
}
|
||||
|
||||
this.messages = translatedMessages.toArray();
|
||||
this.messages = getFacesMessages(clientId).toArray(new FacesMessage[] {});
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return messages.length > currentIndex + 1;
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
public FacesMessage next() {
|
||||
currentIndex++;
|
||||
return messages[currentIndex];
|
||||
}
|
||||
@@ -190,7 +209,7 @@ public class FlowFacesContextMessageDelegate {
|
||||
|
||||
}
|
||||
|
||||
private class ClientIdIterator implements Iterator {
|
||||
private class ClientIdIterator implements Iterator<String> {
|
||||
|
||||
private Message[] messages;
|
||||
|
||||
@@ -204,7 +223,7 @@ public class FlowFacesContextMessageDelegate {
|
||||
return messages.length > currentIndex + 1;
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
public String next() {
|
||||
Message next = messages[++currentIndex];
|
||||
if (next.getSource() == null) {
|
||||
return null;
|
||||
@@ -221,34 +240,46 @@ public class FlowFacesContextMessageDelegate {
|
||||
|
||||
}
|
||||
|
||||
private class SummaryMessageCriteria implements MessageCriteria {
|
||||
private class MatchBySuffixCriteria implements MessageCriteria {
|
||||
|
||||
public boolean test(Message message) {
|
||||
if (message.getSource() == null) {
|
||||
return false;
|
||||
}
|
||||
return message.getSource().toString().endsWith(SUMMARY_MESSAGE_KEY);
|
||||
private String clientId;
|
||||
private String suffix;
|
||||
|
||||
public MatchBySuffixCriteria(String clientId, String suffix) {
|
||||
this.clientId = clientId;
|
||||
this.suffix = suffix;
|
||||
}
|
||||
}
|
||||
|
||||
private class DetailMessageCriteria implements MessageCriteria {
|
||||
|
||||
public boolean test(Message message) {
|
||||
if (message.getSource() == null) {
|
||||
return false;
|
||||
boolean result = false;
|
||||
if (message.getSource() != null) {
|
||||
if (clientId != null) {
|
||||
result = message.getSource().toString().equals(clientId + suffix);
|
||||
} else {
|
||||
result = message.getSource().toString().endsWith(suffix);
|
||||
}
|
||||
}
|
||||
return message.getSource().toString().endsWith(DETAIL_MESSAGE_KEY);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private class UserMessageCriteria implements MessageCriteria {
|
||||
|
||||
private String clientId;
|
||||
|
||||
public UserMessageCriteria(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public boolean test(Message message) {
|
||||
if (message.getSource() == null) {
|
||||
return true;
|
||||
boolean result = false;
|
||||
if (clientId != null) {
|
||||
result = new MatchBySuffixCriteria(clientId, "").test(message);
|
||||
} else {
|
||||
result = (!new MatchBySuffixCriteria(clientId, SUMMARY_MESSAGE_KEY).test(message))
|
||||
&& (!new MatchBySuffixCriteria(clientId, DETAIL_MESSAGE_KEY).test(message));
|
||||
}
|
||||
return !message.getSource().toString().endsWith(SUMMARY_MESSAGE_KEY)
|
||||
&& !message.getSource().toString().endsWith(DETAIL_MESSAGE_KEY);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +287,7 @@ public class FlowFacesContextMessageDelegate {
|
||||
|
||||
String nullSummaryId = null + SUMMARY_MESSAGE_KEY;
|
||||
|
||||
private Set identifiedMessageSources = new HashSet();
|
||||
private Set<String> identifiedMessageSources = new HashSet<String>();
|
||||
|
||||
// From getClientIdsWithMessages docs: If any messages have been queued that were not associated with
|
||||
// any specific client identifier, a null value will be included in the iterated values.
|
||||
@@ -267,7 +298,7 @@ public class FlowFacesContextMessageDelegate {
|
||||
|| message.getSource().equals(nullSummaryId)) {
|
||||
return identifiedMessageSources.add(null);
|
||||
}
|
||||
return identifiedMessageSources.add(message.getSource());
|
||||
return identifiedMessageSources.add((String) message.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ public class FlowSerializedView implements Serializable {
|
||||
return this.componentState;
|
||||
}
|
||||
|
||||
public Object[] asTreeStructAndCompStateArray() {
|
||||
return new Object[] { this.treeStructure, this.componentState };
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("viewId", viewId).toString();
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class FlowViewStateManager extends StateManager {
|
||||
delegate.writeState(context, state); // MyFaces
|
||||
} else if (state instanceof FlowSerializedView) { // Mojarra
|
||||
FlowSerializedView view = (FlowSerializedView) state;
|
||||
delegate.writeState(context, new Object[] { view.getTreeStructure(), view.getComponentState() });
|
||||
delegate.writeState(context, view.asTreeStructAndCompStateArray());
|
||||
} else {
|
||||
super.writeState(context, state);
|
||||
}
|
||||
@@ -173,4 +173,22 @@ public class FlowViewStateManager extends StateManager {
|
||||
return viewRoot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getViewState(FacesContext context) {
|
||||
/*
|
||||
* Mojarra 2: PartialRequestContextImpl.renderState() invokes this method during Ajax request rendering. It is
|
||||
* overridden to convert FlowSerializedView to an array containing tree structure and component state. The
|
||||
* ResponseStateManager.getViewState() calls the ServerSideStateHelper, which expects the array.
|
||||
*/
|
||||
Object state = saveView(context);
|
||||
if (state != null) {
|
||||
if (state instanceof FlowSerializedView) {
|
||||
FlowSerializedView view = (FlowSerializedView) state;
|
||||
state = view.asTreeStructAndCompStateArray();
|
||||
}
|
||||
return context.getRenderKit().getResponseStateManager().getViewState(context, state);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.FactoryFinder;
|
||||
import javax.faces.application.FacesMessage;
|
||||
import javax.faces.application.ProjectStage;
|
||||
import javax.faces.context.ExceptionHandler;
|
||||
@@ -28,20 +29,32 @@ import javax.faces.context.ExternalContext;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.Flash;
|
||||
import javax.faces.context.PartialViewContext;
|
||||
import javax.faces.context.PartialViewContextFactory;
|
||||
import javax.faces.event.PhaseId;
|
||||
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* Extends FlowFacesContext in order to provide JSF 2 delegation method. This is necessary because some of the methods
|
||||
* use JSF 2 specific types as input or output parameters.
|
||||
* Extends FlowFacesContext in order to provide JSF 2 delegation method.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class Jsf2FlowFacesContext extends FlowFacesContext {
|
||||
|
||||
/*
|
||||
* This partialViewContext duplicates the one FacesContextImpl because the constructor of FacesContextImpl calls
|
||||
* getPartialViewContext(), which causes it to be instantiated with an instance of FacesContextImpl. This leads to
|
||||
* issues with adding and showing validation messages during Ajax requests because the FlowFacesContext is
|
||||
* effectively bypassed.
|
||||
*/
|
||||
private PartialViewContext partialViewContext;
|
||||
|
||||
public Jsf2FlowFacesContext(RequestContext context, FacesContext delegate) {
|
||||
super(context, delegate);
|
||||
|
||||
PartialViewContextFactory f = (PartialViewContextFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY);
|
||||
partialViewContext = f.getPartialViewContext(this);
|
||||
}
|
||||
|
||||
public ExternalContext getExternalContext() {
|
||||
@@ -55,15 +68,22 @@ public class Jsf2FlowFacesContext extends FlowFacesContext {
|
||||
}
|
||||
|
||||
public PartialViewContext getPartialViewContext() {
|
||||
return getDelegate().getPartialViewContext();
|
||||
return partialViewContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a List for all Messages in the current MessageContext that does translation to FacesMessages.
|
||||
*/
|
||||
public List<FacesMessage> getMessageList() {
|
||||
return getDelegate().getMessageList();
|
||||
return getMessageDelegate().getMessageList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a List for all Messages with the given clientId in the current MessageContext that does translation to
|
||||
* FacesMessages.
|
||||
*/
|
||||
public List<FacesMessage> getMessageList(String clientId) {
|
||||
return getDelegate().getMessageList(clientId);
|
||||
return getMessageDelegate().getMessageList(clientId);
|
||||
}
|
||||
|
||||
public boolean isPostback() {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.FactoryFinder;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.FacesContextWrapper;
|
||||
import javax.faces.context.PartialResponseWriter;
|
||||
import javax.faces.render.RenderKit;
|
||||
import javax.faces.render.RenderKitFactory;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.js.ajax.AbstractAjaxHandler;
|
||||
|
||||
import com.sun.faces.context.PartialViewContextImpl;
|
||||
|
||||
/**
|
||||
* Ajax handler for JSF 2 requests that can identify JSF 2 Ajax requests and send redirect instructions back to the
|
||||
* client by including a redirect instruction in the content of the response.
|
||||
*
|
||||
* @see AbstractAjaxHandler
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class JsfAjaxHandler extends AbstractAjaxHandler {
|
||||
|
||||
public JsfAjaxHandler() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public JsfAjaxHandler(AbstractAjaxHandler delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
protected boolean isAjaxRequestInternal(HttpServletRequest request, HttpServletResponse response) {
|
||||
String facesRequestHeader = request.getHeader("Faces-Request");
|
||||
return ("partial/ajax".equals(facesRequestHeader)) ? true : false;
|
||||
}
|
||||
|
||||
protected void sendAjaxRedirectInternal(final String targetUrl, final HttpServletRequest request,
|
||||
final HttpServletResponse response, boolean popup) throws IOException {
|
||||
|
||||
// Ideally facesContext.getExternalContext().redirect() should be used instead of the code in this method.
|
||||
// However PartialViewContextImpl.createPartialResponseWriter() calls cxt.getRenderKit(), which in turn
|
||||
// tries to get the UIViewRoot's renderKitId. That results in NPE when JsfAjaxHandler is called
|
||||
// outside of flow execution. The code below wraps the FacesContext to override getRenderKit() and provide a
|
||||
// default render kit id.
|
||||
|
||||
FacesContextHelper helper = new FacesContextHelper();
|
||||
try {
|
||||
FacesContext facesContext = helper.getFacesContext(getServletContext(), request, response);
|
||||
facesContext = new FixedRenderKitFacesContext(facesContext, determineRenderKitId(request, response));
|
||||
PartialViewContextImpl partialViewContext = new PartialViewContextImpl(facesContext);
|
||||
PartialResponseWriter writer = partialViewContext.getPartialResponseWriter();
|
||||
writer.startDocument();
|
||||
writer.redirect(targetUrl);
|
||||
writer.endDocument();
|
||||
} finally {
|
||||
helper.releaseIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
protected String determineRenderKitId(HttpServletRequest request, HttpServletResponse response) {
|
||||
return RenderKitFactory.HTML_BASIC_RENDER_KIT;
|
||||
}
|
||||
|
||||
private class FixedRenderKitFacesContext extends FacesContextWrapper {
|
||||
|
||||
private FacesContext delegate;
|
||||
private String renderKitId;
|
||||
|
||||
public FixedRenderKitFacesContext(FacesContext delegate, String renderKitId) {
|
||||
this.delegate = delegate;
|
||||
this.renderKitId = renderKitId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FacesContext getWrapped() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderKit getRenderKit() {
|
||||
RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
|
||||
return factory.getRenderKit(this, renderKitId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,6 +59,10 @@ public class JsfRuntimeInformation {
|
||||
return jsfVersion >= JSF_12;
|
||||
}
|
||||
|
||||
public static boolean isLessThanJsf20() {
|
||||
return jsfVersion < JSF_20;
|
||||
}
|
||||
|
||||
protected static boolean isMyFacesPresent() {
|
||||
return myFacesPresent;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.faces.webflow;
|
||||
|
||||
import static org.springframework.faces.webflow.JsfRuntimeInformation.isAtLeastJsf12;
|
||||
import static org.springframework.faces.webflow.JsfRuntimeInformation.isAtLeastJsf20;
|
||||
import static org.springframework.faces.webflow.JsfRuntimeInformation.isLessThanJsf20;
|
||||
import static org.springframework.faces.webflow.JsfRuntimeInformation.isPortletRequest;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -37,7 +38,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.faces.ui.AjaxViewRoot;
|
||||
import org.springframework.faces.ui.Jsf2AjaxViewRoot;
|
||||
import org.springframework.js.ajax.SpringJavascriptAjaxHandler;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
@@ -143,12 +143,10 @@ public class JsfViewFactory implements ViewFactory {
|
||||
}
|
||||
|
||||
private JsfView createJsfView(UIViewRoot root, Lifecycle lifecycle, RequestContext context) {
|
||||
if (isSpringJavascriptAjaxRequest(context.getExternalContext())) {
|
||||
AjaxViewRoot viewRoot = (isAtLeastJsf20()) ? new Jsf2AjaxViewRoot(root) : new AjaxViewRoot(root);
|
||||
return new JsfView(viewRoot, lifecycle, context);
|
||||
} else {
|
||||
return new JsfView(root, lifecycle, context);
|
||||
if (isLessThanJsf20() && isSpringJavascriptAjaxRequest(context.getExternalContext())) {
|
||||
root = new AjaxViewRoot(root);
|
||||
}
|
||||
return new JsfView(root, lifecycle, context);
|
||||
}
|
||||
|
||||
private boolean isSpringJavascriptAjaxRequest(ExternalContext context) {
|
||||
|
||||
@@ -153,12 +153,8 @@ public class FlowFacesContextTests extends TestCase {
|
||||
EasyMock.expect(requestContext.getMessageContext()).andStubReturn(messageContext);
|
||||
EasyMock.replay(new Object[] { requestContext });
|
||||
|
||||
int iterationCount = 0;
|
||||
Iterator i = facesContext.getMessages("unknown");
|
||||
while (i.hasNext()) {
|
||||
iterationCount++;
|
||||
}
|
||||
assertEquals(0, iterationCount);
|
||||
assertFalse(i.hasNext());
|
||||
}
|
||||
|
||||
public final void testGetClientIdsWithMessages() {
|
||||
|
||||
@@ -127,7 +127,6 @@ public class JSFMockHelper {
|
||||
FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY, MockRenderKitFactory.class.getName());
|
||||
FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY, MockPartialViewContextFactory.class
|
||||
.getName());
|
||||
externalContext = new MockExternalContext(servletContext, request, response);
|
||||
lifecycleFactory = (MockLifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
|
||||
lifecycle = (MockLifecycle) lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
|
||||
facesContextFactory = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.springframework.faces.webflow;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.myfaces.test.mock.MockPrintWriter;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
|
||||
public class JsfAjaxHandlerTests extends TestCase {
|
||||
|
||||
private JSFMockHelper jsfMock = new JSFMockHelper();
|
||||
private JsfAjaxHandler ajaxHandler;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
jsfMock.setUp();
|
||||
StaticWebApplicationContext webappContext = new StaticWebApplicationContext();
|
||||
webappContext.setServletContext(jsfMock.servletContext());
|
||||
ajaxHandler = new JsfAjaxHandler();
|
||||
ajaxHandler.setApplicationContext(webappContext);
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
jsfMock.tearDown();
|
||||
}
|
||||
|
||||
public void testSendAjaxRedirect() throws Exception {
|
||||
ajaxHandler.sendAjaxRedirectInternal("/target", jsfMock.request(), jsfMock.response(), false);
|
||||
assertEquals(
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<partial-response><redirect url=\"/target\"/></partial-response>",
|
||||
extractResponseContent());
|
||||
}
|
||||
|
||||
private String extractResponseContent() throws IOException {
|
||||
MockPrintWriter writer = (MockPrintWriter) jsfMock.response().getWriter();
|
||||
return new String(writer.content());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import org.easymock.EasyMock;
|
||||
import org.jboss.el.ExpressionFactoryImpl;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.support.FluentParserContext;
|
||||
import org.springframework.faces.ui.AjaxViewRoot;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
@@ -218,7 +217,7 @@ public class JsfViewFactoryTests extends TestCase {
|
||||
|
||||
assertNotNull("A View was not restored", restoredView);
|
||||
assertTrue("A JsfView was expected", restoredView instanceof JsfView);
|
||||
assertTrue("An AjaxViewRoot was not set", ((JsfView) restoredView).getViewRoot() instanceof AjaxViewRoot);
|
||||
assertTrue("An ViewRoot was not set", ((JsfView) restoredView).getViewRoot() instanceof UIViewRoot);
|
||||
assertEquals("View name did not match", VIEW_ID, ((JsfView) restoredView).getViewRoot().getViewId());
|
||||
assertFalse("An unexpected event was signaled,", restoredView.hasFlowEvent());
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.myfaces.test.mock.MockExternalContext;
|
||||
import org.apache.myfaces.test.mock.MockExternalContext20;
|
||||
|
||||
public class MockBaseFacesContextFactory extends FacesContextFactory {
|
||||
|
||||
@@ -21,7 +21,7 @@ public class MockBaseFacesContextFactory extends FacesContextFactory {
|
||||
return FacesContext.getCurrentInstance();
|
||||
} else {
|
||||
|
||||
ExternalContext ext = new MockExternalContext((ServletContext) context, (HttpServletRequest) request,
|
||||
ExternalContext ext = new MockExternalContext20((ServletContext) context, (HttpServletRequest) request,
|
||||
(HttpServletResponse) response);
|
||||
|
||||
return new MockBaseFacesContext(ext, lifecycle);
|
||||
|
||||
Reference in New Issue
Block a user