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:
Rossen Stoyanchev
2010-07-28 12:56:24 +00:00
parent 507fec3edf
commit fe9229cf15
36 changed files with 722 additions and 494 deletions

View File

@@ -4,9 +4,11 @@ http://www.springframework.org/webflow
Changes in version 2.2.0.RELEASE ()
-----------------------------------
* Add support for handling JSF 2 resource requests (SWF-1366)
* Add support for JSF 2 Ajax requests (SWF-1367)
Changes in version 2.1.0.RELEASE (July 19, 2010)
Changes in version 2.1.1.RELEASE (July 19, 2010)
------------------------------------------------
* Extract JSF 2 methods from FlowApplication into Jsf2FlowApplication (SWF-1261).
* Fix handling of cascaded attributes in AjaxTilesView (SWF-1053).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
package org.springframework.js.ajax;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.support.WebApplicationObjectSupport;
public abstract class AbstractAjaxHandler extends WebApplicationObjectSupport implements AjaxHandler {
private AbstractAjaxHandler delegate;
public AbstractAjaxHandler(AbstractAjaxHandler delegate) {
this.delegate = delegate;
}
public final boolean isAjaxRequest(HttpServletRequest request, HttpServletResponse response) {
if (isAjaxRequestInternal(request, response)) {
return true;
}
if (delegate != null) {
return delegate.isAjaxRequest(request, response);
}
return false;
}
public final void sendAjaxRedirect(String targetUrl, HttpServletRequest request, HttpServletResponse response,
boolean popup) throws IOException {
if (isAjaxRequestInternal(request, response)) {
sendAjaxRedirectInternal(targetUrl, request, response, popup);
}
if (delegate != null) {
delegate.sendAjaxRedirect(targetUrl, request, response, popup);
}
}
protected abstract boolean isAjaxRequestInternal(HttpServletRequest request, HttpServletResponse response);
protected abstract void sendAjaxRedirectInternal(String targetUrl, HttpServletRequest request,
HttpServletResponse response, boolean popup) throws IOException;
}

View File

@@ -25,10 +25,12 @@ import org.springframework.util.StringUtils;
/**
* Ajax handler for Spring Javascript (Spring.js).
*
* @see AbstractAjaxHandler
*
* @author Jeremy Grelle
* @author Keith Donald
*/
public class SpringJavascriptAjaxHandler implements AjaxHandler {
public class SpringJavascriptAjaxHandler extends AbstractAjaxHandler {
/**
* The response header to be set on an Ajax redirect
@@ -50,7 +52,21 @@ public class SpringJavascriptAjaxHandler implements AjaxHandler {
*/
public static final String AJAX_SOURCE_PARAM = "ajaxSource";
public boolean isAjaxRequest(HttpServletRequest request, HttpServletResponse response) {
/**
* Create a SpringJavascriptAjaxHandler that is not part of a chain of AjaxHandler's.
*/
public SpringJavascriptAjaxHandler() {
this(null);
}
/**
* Create a SpringJavascriptAjaxHandler as part of a chain of AjaxHandler's.
*/
public SpringJavascriptAjaxHandler(AbstractAjaxHandler delegate) {
super(delegate);
}
protected boolean isAjaxRequestInternal(HttpServletRequest request, HttpServletResponse response) {
String acceptHeader = request.getHeader("Accept");
String ajaxParam = request.getParameter(AJAX_SOURCE_PARAM);
if (AJAX_ACCEPT_CONTENT_TYPE.equals(acceptHeader) || StringUtils.hasText(ajaxParam)) {
@@ -60,7 +76,7 @@ public class SpringJavascriptAjaxHandler implements AjaxHandler {
}
}
public void sendAjaxRedirect(String targetUrl, HttpServletRequest request, HttpServletResponse response,
protected void sendAjaxRedirectInternal(String targetUrl, HttpServletRequest request, HttpServletResponse response,
boolean popup) throws IOException {
if (popup) {
response.setHeader(POPUP_VIEW_HEADER, "true");

View File

@@ -0,0 +1,90 @@
package org.springframework.js.ajax;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class AbstractAjaxHandlerTests extends TestCase {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
protected void setUp() throws Exception {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
public void testIsAjaxRequest() throws Exception {
TestAjaxHandler handler = new TestAjaxHandler(null, true);
assertTrue(handler.isAjaxRequest(request, response));
}
public void testIsNotAjaxRequest() throws Exception {
TestAjaxHandler handler = new TestAjaxHandler(null, false);
assertFalse(handler.isAjaxRequest(request, response));
}
public void testIsAjaxRequestViaDelegate() throws Exception {
TestAjaxHandler handler = new TestAjaxHandler(new TestAjaxHandler(null, true), false);
assertTrue(handler.isAjaxRequest(request, response));
}
public void testSendAjaxRedirect() throws Exception {
TestAjaxHandler handler = new TestAjaxHandler(null, true);
handler.sendAjaxRedirect("", request, response, false);
assertTrue(handler.wasAjaxRedirectInternalCalled);
}
public void testAjaxRedirectNotSent() throws Exception {
TestAjaxHandler handler = new TestAjaxHandler(null, false);
handler.sendAjaxRedirect("", request, response, false);
assertFalse(handler.wasAjaxRedirectInternalCalled());
}
public void testSendAjaxRedirectViaDelegate() throws Exception {
TestAjaxHandler handler = new TestAjaxHandler(new TestAjaxHandler(null, true), false);
handler.sendAjaxRedirect("", request, response, false);
assertTrue(handler.wasAjaxRedirectInternalCalled());
}
private final class TestAjaxHandler extends AbstractAjaxHandler {
private boolean isAjaxRequest;
private boolean wasAjaxRedirectInternalCalled;
private TestAjaxHandler delegate;
private TestAjaxHandler(TestAjaxHandler delegate, boolean isAjaxRequest) {
super(delegate);
this.delegate = delegate;
this.isAjaxRequest = isAjaxRequest;
}
protected boolean isAjaxRequestInternal(HttpServletRequest request, HttpServletResponse response) {
return isAjaxRequest;
}
protected void sendAjaxRedirectInternal(String targetUrl, HttpServletRequest request,
HttpServletResponse response, boolean popup) throws IOException {
wasAjaxRedirectInternalCalled = true;
}
public TestAjaxHandler getDelegate() {
return delegate;
}
public boolean wasAjaxRedirectInternalCalled() {
if (wasAjaxRedirectInternalCalled) {
return true;
} else {
return (getDelegate() != null) ? delegate.wasAjaxRedirectInternalCalled() : false;
}
}
}
}

View File

@@ -31,7 +31,7 @@ public class ResourceServletTests extends TestCase {
public final void testExecute() throws Exception {
String requestPath = "/dojo/dojo.js";
String requestPath = "/spring/Spring.js";
request.setPathInfo(requestPath);
servlet.doGet(request, response);
@@ -41,10 +41,10 @@ public class ResourceServletTests extends TestCase {
public final void testExecute_CombinedResources() throws Exception {
String requestPath = "/dojo/dojo.js";
String requestPath = "/spring/Spring.js";
request.setPathInfo(requestPath);
Map params = new HashMap();
params.put("appended", "/dijit/dijit.js,/dijit/Dialog.js");
params.put("appended", "/spring/Spring-Dojo.js");
request.setParameters(params);
servlet.doGet(request, response);
@@ -53,7 +53,7 @@ public class ResourceServletTests extends TestCase {
public final void testExecute_CompressedResponse() throws Exception {
String requestPath = "/dojo/dojo.js";
String requestPath = "/spring/Spring.js";
request.setPathInfo(requestPath);
request.addHeader("Accept-Encoding", "gzip");
servlet.doGet(request, response);

View File

@@ -1,13 +1,27 @@
#Sun Apr 27 09:05:45 EDT 2008
#Wed Jul 28 13:06:21 BST 2010
eclipse.preferences.version=1
instance/org.eclipse.core.net/org.eclipse.core.net.hasMigrated=true
org.springframework.ide.eclipse.core.builders.enable.aopreferencemodelbuilder=true
org.springframework.ide.eclipse.core.builders.enable.beanmetadatabuilder=true
org.springframework.ide.eclipse.core.builders.enable.osgibundleupdater=true
org.springframework.ide.eclipse.core.enable.project.preferences=true
org.springframework.ide.eclipse.core.validator.enable.com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.bestpractices.beansvalidator=true
org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.server.quickfix.manifestvalidator=false
org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.beans.core.beansvalidator=true
org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.core.springvalidator=true
org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.webflow.core.validator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivationPolicyRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivatorRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleManifestVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.exportPackageRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.importRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.parsingProblemsRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.requireBundleRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.AvoidDriverManagerDataSource-com.springsource.sts.bestpractices.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ImportElementsAtTopRulee-com.springsource.sts.bestpractices.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ParentBeanSpecifiesAbstractClassRule-com.springsource.sts.bestpractices.beansvalidator=true
@@ -16,6 +30,11 @@ org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UnnecessaryValueElementRule-com.springsource.sts.bestpractices.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UseBeanInheritance-com.springsource.sts.bestpractices.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.legacyxmlusage.jndiobjectfactory-com.springsource.sts.bestpractices.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importLibraryVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importPackageVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.requireBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.autowire.autowire-org.springframework.ide.eclipse.beans.core.beansvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanAlias-org.springframework.ide.eclipse.beans.core.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanClass-org.springframework.ide.eclipse.beans.core.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanConstructorArgument-org.springframework.ide.eclipse.beans.core.beansvalidator=true
@@ -28,6 +47,7 @@ org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.i
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.methodOverride-org.springframework.ide.eclipse.beans.core.beansvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.parsingProblems-org.springframework.ide.eclipse.beans.core.beansvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.requiredProperty-org.springframework.ide.eclipse.beans.core.beansvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.toolAnnotation-org.springframework.ide.eclipse.beans.core.beansvalidator=false
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.core.springClasspath-org.springframework.ide.eclipse.core.springvalidator=true
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.action-org.springframework.ide.eclipse.webflow.core.validator=true
org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.actionstate-org.springframework.ide.eclipse.webflow.core.validator=true

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<webflow-project-description>
<version>1</version>
<pluginVersion><![CDATA[2.2.1.v200811281800]]></pluginVersion>
<pluginVersion><![CDATA[2.3.3.201005082200-CI-R3771-B737]]></pluginVersion>
<configs>
<config>
<file>src/main/webapp/WEB-INF/flows/booking/booking-flow.xml</file>
@@ -10,8 +10,7 @@
</config>
<config>
<file>src/main/webapp/WEB-INF/flows/main/main-flow.xml</file>
<name><![CDATA[main]]></name>
<beans-config><![CDATA[1:BeansModel|2:swf-booking-faces|3:src/main/webapp/WEB-INF/config/web-application-config.xml]]></beans-config>
<name><![CDATA[main-flow]]></name>
</config>
</configs>
</webflow-project-description>

View File

@@ -30,8 +30,10 @@
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.2.6.ga" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.3.0.ga" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.1.ga" conf="compile->runtime"/>
<dependency org="org.hibernate" name="com.springsource.org.hibernate.validator" rev="4.1.0.GA" />
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="compile->runtime"/>
<dependency org="org.joda" name="com.springsource.org.joda.time" rev="1.6.0" conf="compile->runtime"/>
<dependency org="org.slf4j" name="com.springsource.slf4j.log4j" rev="1.5.6" />
<dependency org="org.springframework" name="org.springframework.aop" rev="3.0.3.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.beans" rev="3.0.3.RELEASE" conf="compile->runtime"/>
<dependency org="org.springframework" name="org.springframework.context" rev="3.0.3.RELEASE" conf="compile->runtime"/>

View File

@@ -42,6 +42,11 @@
<artifactId>com.springsource.org.hibernate.ejb</artifactId>
<version>3.3.1.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate.validator</artifactId>
<version>4.1.0.GA</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>com.springsource.org.hsqldb</artifactId>
@@ -52,6 +57,11 @@
<artifactId>joda-time</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>com.springsource.slf4j.log4j</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.aop</artifactId>

View File

@@ -15,6 +15,9 @@ import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
@@ -34,12 +37,17 @@ public class Booking implements Serializable {
private Hotel hotel;
@NotNull
private Date checkinDate;
@NotNull
private Date checkoutDate;
@Pattern(regexp = "[0-9]{16}", message = "Credit card number must be 16 digits.")
private String creditCard;
@NotNull
@Size(min = 3, message = "A valid credit card name is required.")
private String creditCardName;
private int creditCardExpiryMonth;

View File

@@ -36,6 +36,9 @@
<!-- Dispatches requests mapped to flows to FlowHandler implementations -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor" />
<property name="ajaxHandler">
<bean class="org.springframework.faces.webflow.JsfAjaxHandler"/>
</property>
</bean>
<!-- Dispatches requests mapped to org.springframework.web.HttpRequestHandler implementations -->

View File

@@ -3,11 +3,9 @@
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:sf="http://www.springframework.org/tags/faces"
template="/WEB-INF/layouts/standard.xhtml">
<ui:define name="content">
<div class="span-5">
<h3>${booking.hotel.name}</h3>
<address>
@@ -18,12 +16,8 @@
${booking.hotel.country}
</address>
</div>
<div class="span-12">
<ui:fragment id="messages">
<div id="messagesInsertionPoint">
<h:messages errorClass="error" layout="table"/>
</div>
</ui:fragment>
<h:panelGroup id="bookingsFragment" styleClass="span-12" layout="block">
<h:messages errorClass="error" layout="table"/>
<h:form id="bookingForm">
<fieldset>
<legend>Book Hotel</legend>
@@ -45,11 +39,9 @@
</div>
<div class="span-7 last">
<p>
<sf:clientDateValidator required="true">
<h:inputText id="checkinDate" value="#{booking.checkinDate}" required="true">
<f:convertDateTime pattern="MM-dd-yyyy" timeZone="EST"/>
</h:inputText>
</sf:clientDateValidator>
<h:inputText id="checkinDate" value="#{booking.checkinDate}" required="true">
<f:convertDateTime pattern="MM-dd-yyyy" timeZone="EST"/>
</h:inputText>
</p>
</div>
</div>
@@ -59,11 +51,9 @@
</div>
<div class="span-7 last">
<p>
<sf:clientDateValidator required="true">
<h:inputText id="checkoutDate" value="#{booking.checkoutDate}" required="true">
<f:convertDateTime pattern="MM-dd-yyyy" timeZone="EST"/>
</h:inputText>
</sf:clientDateValidator>
<h:inputText id="checkoutDate" value="#{booking.checkoutDate}" required="true">
<f:convertDateTime pattern="MM-dd-yyyy" timeZone="EST"/>
</h:inputText>
</p>
</div>
</div>
@@ -116,9 +106,7 @@
</div>
<div class="span-7 last">
<p>
<sf:clientTextValidator required="true" regExp="[0-9]{16}" invalidMessage="A 16-digit credit card number is required.">
<h:inputText id="creditCard" value="#{booking.creditCard}" required="true"/>
</sf:clientTextValidator>
<h:inputText id="creditCard" value="#{booking.creditCard}"/>
</p>
</div>
</div>
@@ -128,9 +116,7 @@
</div>
<div class="span-7 last">
<p>
<sf:clientTextValidator required="true">
<h:inputText id="creditCardName" value="#{booking.creditCardName}" required="true"/>
</sf:clientTextValidator>
<h:inputText id="creditCardName" value="#{booking.creditCardName}"/>
</p>
</div>
</div>
@@ -150,14 +136,13 @@
</div>
</div>
<div>
<sf:validateAllOnClick>
<sf:commandButton id="proceed" action="proceed" processIds="*" value="Proceed"/>&#160;
</sf:validateAllOnClick>
<sf:commandButton id="cancel" value="Cancel" action="cancel"/>
<h:commandButton id="proceed" action="proceed" value="Proceed">
<f:ajax render=":bookingsFragment" execute="@form" />
</h:commandButton>&#160;
<h:commandButton id="cancel" value="Cancel" action="cancel" immediate="true" />
</div>
</fieldset>
</h:form>
</div>
</h:panelGroup>
</ui:define>
</ui:composition>

View File

@@ -3,7 +3,6 @@
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:sf="http://www.springframework.org/tags/faces"
template="/WEB-INF/layouts/standard.xhtml">
<ui:define name="content">
@@ -70,7 +69,7 @@
<div>
<h:commandButton id="confirm" value="Confirm" action="confirm"/>&#160;
<h:commandButton id="revise" value="Revise" action="revise"/>&#160;
<h:commandButton id="cancel" value="Cancel" action="cancel"/>
<h:commandButton id="cancel" value="Cancel" action="cancel" />
</div>
</fieldset>
</h:form>

View File

@@ -3,81 +3,78 @@
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:sf="http://www.springframework.org/tags/faces"
template="/WEB-INF/layouts/standard.xhtml">
<ui:define name="content">
<ui:fragment id="hotelSearchFragment">
<div id="hotelSearch">
<h2>Search Hotels</h2>
<span class="errors">
<h:messages globalOnly="true" />
</span>
<h:form id="mainForm" styleClass="inline">
<fieldset>
<div class="span-8">
<h:outputLabel for="searchString">Search String: </h:outputLabel>
<sf:clientTextValidator promptMessage="Search hotels by name, address, city, or zip.">
<h:inputText id="searchString" value="#{searchCriteria.searchString}" />
</sf:clientTextValidator>
</div>
<div class="span-6">
<h:outputLabel for="pageSize">Maximum Results: </h:outputLabel>
<h:selectOneMenu id="pageSize" value="#{searchCriteria.pageSize}">
<f:selectItems value="#{referenceData.pageSizeOptions}" />
</h:selectOneMenu>
</div>
<div class="span-3 last">
<sf:commandButton id="findHotels" value="Find Hotels" processIds="*" action="search" />
</div>
</fieldset>
</h:form>
</div>
</ui:fragment>
<h:panelGroup id="hotelSearch" layout="block">
<h2>Search Hotels</h2>
<span class="errors">
<h:messages globalOnly="true" />
</span>
<h:form id="mainForm" styleClass="inline">
<fieldset>
<div class="span-8">
<h:outputLabel for="searchString">Search String: </h:outputLabel>
<h:inputText id="searchString" value="#{searchCriteria.searchString}" />
</div>
<div class="span-6">
<h:outputLabel for="pageSize">Maximum Results: </h:outputLabel>
<h:selectOneMenu id="pageSize" value="#{searchCriteria.pageSize}">
<f:selectItems value="#{referenceData.pageSizeOptions}" />
</h:selectOneMenu>
</div>
<div class="span-3 last">
<h:commandButton id="findHotels" value="Find Hotels" action="search" >
<f:ajax render="@form" execute="@form" />
</h:commandButton>
</div>
</fieldset>
</h:form>
</h:panelGroup>
<ui:fragment id="bookingsFragment" rendered="#{currentUser!=null}">
<div id="bookingsSection">
<h2>Current Hotel Bookings</h2>
<h:outputText value="No Bookings Found" rendered="#{bookings.rowCount==0}"/>
<h:form id="bookingsForm" rendered="#{bookings.rowCount > 0}">
<h:dataTable id="bookings" styleClass="summary" value="#{bookings}" var="booking">
<h:column>
<f:facet name="header">Name</f:facet>
#{booking.hotel.name}
</h:column>
<h:column>
<f:facet name="header">Address</f:facet>
#{booking.hotel.address}
</h:column>
<h:column>
<f:facet name="header">City, State</f:facet>
#{booking.hotel.city}, #{booking.hotel.state}
</h:column>
<h:column>
<f:facet name="header">Check in date</f:facet>
<h:outputText value="#{booking.checkinDate}">
<f:convertDateTime dateStyle="short"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Check out date</f:facet>
<h:outputText value="#{booking.checkoutDate}">
<f:convertDateTime dateStyle="short"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Confirmation number</f:facet>
#{booking.id}
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<sf:commandLink id="cancel" value="Cancel" processIds="bookingsFragment" action="cancelBooking" />
</h:column>
</h:dataTable>
</h:form>
</div>
</ui:fragment>
<h:panelGroup id="bookingsFragment" rendered="#{currentUser!=null}" layout="block">
<h2>Current Hotel Bookings</h2>
<h:outputText value="No Bookings Found" rendered="#{bookings.rowCount==0}"/>
<h:form id="bookingsForm" rendered="#{bookings.rowCount > 0}">
<h:dataTable id="bookings" styleClass="summary" value="#{bookings}" var="booking">
<h:column>
<f:facet name="header">Name</f:facet>
#{booking.hotel.name}
</h:column>
<h:column>
<f:facet name="header">Address</f:facet>
#{booking.hotel.address}
</h:column>
<h:column>
<f:facet name="header">City, State</f:facet>
#{booking.hotel.city}, #{booking.hotel.state}
</h:column>
<h:column>
<f:facet name="header">Check in date</f:facet>
<h:outputText value="#{booking.checkinDate}">
<f:convertDateTime dateStyle="short"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Check out date</f:facet>
<h:outputText value="#{booking.checkoutDate}">
<f:convertDateTime dateStyle="short"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">Confirmation number</f:facet>
#{booking.id}
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink id="cancel" value="Cancel" action="cancelBooking">
<f:ajax render=":bookingsFragment" />
</h:commandLink>
</h:column>
</h:dataTable>
</h:form>
</h:panelGroup>
</ui:define>
</ui:composition>

View File

@@ -24,20 +24,17 @@
</on-render>
<transition on="sort">
<set name="searchCriteria.sortBy" value="requestParameters.sortBy" />
<render fragments="hotels:searchResultsFragment" />
</transition>
<transition on="previous">
<evaluate expression="searchCriteria.previousPage()" />
<render fragments="hotels:searchResultsFragment" />
</transition>
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
<render fragments="hotels:searchResultsFragment" />
</transition>
<transition on="select" to="reviewHotel">
<set name="flowScope.hotel" value="hotels.selectedRow" />
</transition>
<transition on="changeSearch" to="changeSearchCriteria" />
<transition on="changeSearch" to="enterSearchCriteria" />
</view-state>
<view-state id="reviewHotel">
@@ -50,15 +47,6 @@
<transition on="bookingConfirmed" to="finish" />
<transition on="bookingCancelled" to="enterSearchCriteria" />
</subflow-state>
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
<on-entry>
<render fragments="hotelSearchFragment" />
</on-entry>
<transition on="search" to="reviewHotels">
<evaluate expression="searchCriteria.resetPage()"/>
</transition>
</view-state>
<end-state id="finish" />

View File

@@ -3,7 +3,6 @@
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:sf="http://www.springframework.org/tags/faces"
template="/WEB-INF/layouts/standard.xhtml">
<ui:define name="content">

View File

@@ -3,60 +3,60 @@
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:sf="http://www.springframework.org/tags/faces"
template="/WEB-INF/layouts/standard.xhtml">
<ui:define name="content">
<h:form id="hotels">
<h:form id="hotelResults">
<div>
<h2>Hotel Results</h2>
<p>
<sf:commandLink value="Change Search" action="changeSearch"/>
<h:commandLink value="Change Search" action="changeSearch"/>
</p>
<ui:fragment id="searchResultsFragment">
<div id="searchResults">
<h:outputText id="noHotelsText" value="No Hotels Found" rendered="#{hotels.rowCount == 0}"/>
<h:dataTable id="hotels" styleClass="summary" value="#{hotels}" var="h" rendered="#{hotels.rowCount > 0}">
<h:column>
<f:facet name="header">
<sf:commandLink id="sortByNameLink" action="sort" value="Name" processIds="*">
<f:param name="sortBy" value="name" />
</sf:commandLink>
</f:facet>
#{h.name}
</h:column>
<h:column>
<f:facet name="header">Address</f:facet>
#{h.address}
</h:column>
<h:column>
<f:facet name="header">
<sf:commandLink id="sortByCity" action="sort" value="City, State" processIds="*" >
<f:param name="sortBy" value="city" />
</sf:commandLink>
</f:facet>
#{h.city}, #{h.state}, #{h.country}
</h:column>
<h:column>
<f:facet name="header">Zip</f:facet>
#{h.zip}
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<sf:commandLink id="viewHotelLink" value="View Hotel" action="select"/>
</h:column>
</h:dataTable>
<div class="buttonGroup">
<div class="span-3">
<sf:commandLink id="previousPageLink" value="Previous results" action="previous" rendered="#{searchCriteria.page > 0}"/>
</div>
<div class="span-15 last">
<sf:commandLink id="nextPageLink" value="More Results" action="next" rendered="#{not empty hotels and hotels.rowCount == searchCriteria.pageSize}"/>
</div>
</div>
<div id="searchResults">
<h:outputText id="noHotelsText" value="No Hotels Found" rendered="#{hotels.rowCount == 0}"/>
<h:dataTable id="hotels" styleClass="summary" value="#{hotels}" var="h" rendered="#{hotels.rowCount > 0}">
<h:column>
<f:facet name="header">
<h:commandLink id="sortByNameLink" action="sort" value="Name">
<f:param name="sortBy" value="name" />
<f:ajax render="hotels" />
</h:commandLink>
</f:facet>
#{h.name}
</h:column>
<h:column>
<f:facet name="header">Address</f:facet>
#{h.address}
</h:column>
<h:column>
<f:facet name="header">
<h:commandLink id="sortByCity" action="sort" value="City, State">
<f:param name="sortBy" value="city" />
<f:ajax render="hotels" />
</h:commandLink>
</f:facet>
#{h.city}, #{h.state}, #{h.country}
</h:column>
<h:column>
<f:facet name="header">Zip</f:facet>
#{h.zip}
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink id="viewHotelLink" value="View Hotel" action="select"/>
</h:column>
</h:dataTable>
<div class="buttonGroup">
<h:commandLink id="previousPageLink" value="Previous Results" action="previous" rendered="#{searchCriteria.page > 0}">
<f:ajax render="@form" />
</h:commandLink>
&nbsp;
<h:commandLink id="nextPageLink" value="More Results" action="next" rendered="#{not empty hotels and hotels.rowCount == searchCriteria.pageSize}">
<f:ajax render="@form" />
</h:commandLink>
</div>
</ui:fragment>
</div>
</div>
</h:form>
</ui:define>

View File

@@ -7,7 +7,7 @@
xmlns:sf="http://www.springframework.org/tags/faces"
contentType="text/html" encoding="UTF-8">
<html>
<head>
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Spring Faces: Hotel Booking Sample Application</title>
<sf:includeStyles/>
@@ -18,7 +18,7 @@
<![endif]-->
<link rel="stylesheet" href="${request.contextPath}/styles/booking.css" type="text/css" media="screen" />
<ui:insert name="headIncludes"/>
</head>
</h:head>
<body class="tundra">
<div id="page" class="container">
<div id="header">

View File

@@ -17,7 +17,7 @@ input[type=submit] {
color: #fff;
background: #fff url(../images/btn.bg.gif) 0 0 repeat-x;
border-style: none;
margin:0.4em 0;
margin:0.4em 0.4em 0.4em 0;
height: 1.8em;
}