SWF-1243 Comprehensive update to spring-faces for JSF 2.0 support

This commit is contained in:
Rossen Stoyanchev
2010-06-02 19:48:10 +00:00
parent acc793c6a2
commit f9f9fd094b
21 changed files with 674 additions and 78 deletions

View File

@@ -10,6 +10,9 @@
<url name="java-net-maven2" m2compatible="true">
<artifact pattern="http://download.java.net/maven/2/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"/>
</url>
<url name="apache-maven2-snapshot" m2compatible="true">
<artifact pattern="http://people.apache.org/repo/m2-snapshot-repository/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"/>
</url>
<ibiblio name="maven2" m2compatible="true"/>
</chain>
</resolvers>

View File

@@ -8,7 +8,6 @@
<classpathentry combineaccessrules="false" kind="src" path="/spring-js"/>
<classpathentry combineaccessrules="false" kind="src" path="/spring-webflow"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="var" path="IVY_CACHE/com.sun.facelets/com.springsource.com.sun.facelets/1.1.14/com.springsource.com.sun.facelets-1.1.14.jar" sourcepath="IVY_CACHE/com.sun.facelets/com.springsource.com.sun.facelets/1.1.14/com.springsource.com.sun.facelets-sources-1.1.14.jar"/>
<classpathentry kind="var" path="IVY_CACHE/com.sun.faces/jsf-impl/2.0.2/jsf-impl-2.0.2.jar" sourcepath="/IVY_CACHE/com.sun.faces/jsf-impl/2.0.2/jsf-impl-2.0.2-sources.jar"/>
<classpathentry kind="var" path="IVY_CACHE/javax.faces/jsf-api/2.0/jsf-api-2.0.jar" sourcepath="/IVY_CACHE/javax.faces/jsf-api/2.0/jsf-api-2.0-sources.jar"/>
<classpathentry kind="var" path="IVY_CACHE/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-1.0.0.jar" sourcepath="IVY_CACHE/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-sources-1.0.0.jar"/>

View File

@@ -11,9 +11,12 @@
<configurations>
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
<conf name="jsf20" visibility="public"/>
<conf name="jsf12" visibility="public"/>
<conf name="mojarra20" visibility="public"/>
<conf name="myfaces20" visibility="public"/>
<conf name="mojarra12" visibility="public"/>
<conf name="myfaces12" visibility="public"/>
<conf name="jsf11" visibility="public"/>
<conf name="myfacesPortlet20" visibility="public"/>
</configurations>
<publications>
@@ -38,18 +41,35 @@
<dependency org="org.springframework.webflow" name="org.springframework.js" rev="latest.integration" conf="compile->runtime"/>
<dependency org="org.springframework.webflow" name="org.springframework.webflow" rev="latest.integration" conf="compile->runtime"/>
<!-- JSF 2.0 dependencies -->
<dependency org="javax.faces" name="jsf-api" rev="2.0" conf="provided,jsf20->runtime(*)" />
<dependency org="com.sun.faces" name="jsf-impl" rev="2.0.2" conf="provided,jsf20->runtime(*)" />
<!-- JSF 2.0 Mojarra dependencies (no ivy configuration: downloaded from maven repository) -->
<dependency org="javax.faces" name="jsf-api" rev="2.0" conf="provided, mojarra20->runtime(*)" />
<dependency org="com.sun.faces" name="jsf-impl" rev="2.0.2" conf="provided, mojarra20->runtime(*)" />
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp.jstl" rev="1.2.0" conf="test, mojarra20->runtime"/>
<!-- JSF 1.2 dependencies -->
<dependency org="javax.faces" name="com.springsource.javax.faces" rev="1.2.0.09" conf="provided, jsf12->runtime" />
<dependency org="com.sun.faces" name="com.springsource.com.sun.faces" rev="1.2.0.09" conf="provided, jsf12->runtime" />
<!-- JSF 2.0 MyFaces dependencies (no ivy configuration: downloaded from maven repository) -->
<dependency org="org.apache.myfaces.core" name="myfaces-api" rev="2.0.1-SNAPSHOT" conf="provided, myfaces20->runtime(*)" />
<dependency org="org.apache.myfaces.core" name="myfaces-impl" rev="2.0.1-SNAPSHOT" conf="provided, myfaces20->runtime(*)" />
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.discovery" rev="0.4.0" conf="provided, myfaces20->runtime(*)" />
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.digester" rev="1.8.0" conf="provided, myfaces20->runtime(*)" />
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.codec" rev="1.3.0" conf="provided, myfaces20->runtime(*)" />
<dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp.jstl" rev="1.2.0" conf="test, mojarra20->runtime"/>
<!-- JSF 1.2 Mojarra dependencies -->
<dependency org="javax.faces" name="com.springsource.javax.faces" rev="1.2.0.09" conf="provided, mojarra12->runtime" />
<dependency org="com.sun.faces" name="com.springsource.com.sun.faces" rev="1.2.0.09" conf="provided, mojarra12->runtime" />
<!-- JSF 1.1 dependencies -->
<!-- JSF 1.2 MyFaces dependencies -->
<dependency org="org.apache.myfaces" name="com.springsource.org.apache.myfaces.javax.faces" rev="1.2.2" conf="myfaces12->runtime" />
<dependency org="org.apache.myfaces" name="com.springsource.org.apache.myfaces" rev="1.2.2" conf="myfaces12->runtime" />
<!-- JSF 1.1 MyFaces dependencies -->
<dependency org="org.apache.myfaces" name="com.springsource.org.apache.myfaces.javax.faces" rev="1.1.5" conf="jsf11->runtime" />
<dependency org="org.apache.myfaces" name="com.springsource.org.apache.myfaces" rev="1.1.5" conf="jsf11->runtime" />
<!-- MyFaces JSF-Portlet Bridge for Portlet 2.0 and JSF 1.2 (no ivy configuration: downloaded via maven) -->
<dependency org="org.apache.myfaces.portlet-bridge" name="portlet-bridge-api" rev="2.0.0-alpha-2" conf="provided, myfacesPortlet20->runtime(*)" />
<dependency org="org.apache.myfaces.portlet-bridge" name="portlet-bridge-impl" rev="2.0.0-alpha-2" conf="provided, myfacesPortlet20->runtime(*)" />
<!-- test time only dependencies -->
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="test->runtime" />
<dependency org="org.apache.shale" name="com.springsource.org.apache.shale.test" rev="1.0.4" conf="test->runtime" />

View File

@@ -15,11 +15,12 @@
*/
package org.springframework.faces.mvc;
import java.io.IOException;
import static org.springframework.faces.webflow.JsfRuntimeInformation.isAtLeastJsf12;
import static org.springframework.faces.webflow.JsfRuntimeInformation.isPortletRequest;
import java.util.Iterator;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
@@ -62,7 +63,7 @@ public class JsfView extends AbstractUrlBasedView {
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
if (JsfUtils.isAtLeastJsf12() && !JsfUtils.isPortlet(facesContext)) {
if (isAtLeastJsf12() && (!isPortletRequest(facesContext))) {
viewHandler.initView(facesContext);
}
@@ -78,12 +79,8 @@ public class JsfView extends AbstractUrlBasedView {
facesContext.setViewRoot(viewRoot);
facesContext.renderResponse();
try {
JsfUtils.notifyBeforeListeners(PhaseId.RENDER_RESPONSE, facesLifecycle, facesContext);
logger.debug("Asking view handler to render view");
facesContext.getApplication().getViewHandler().renderView(facesContext, viewRoot);
JsfUtils.notifyAfterListeners(PhaseId.RENDER_RESPONSE, facesLifecycle, facesContext);
} catch (IOException e) {
throw new FacesException("An I/O error occurred during view rendering", e);
logger.debug("Asking faces lifecycle to render");
facesLifecycle.render(facesContext);
} finally {
logger.debug("View rendering complete");
facesContext.responseComplete();

View File

@@ -194,9 +194,18 @@ public class AjaxViewRoot extends DelegatingViewRoot {
private void swapChildren(UIViewRoot source, UIViewRoot target) {
target.getChildren().addAll(source.getChildren());
Iterator i = target.getChildren().iterator();
while (i.hasNext()) {
UIComponent child = (UIComponent) i.next();
// Create a new list because the children of ViewRoot can change while we're iterating.
// For example:
// 1. child is an outputScript component with target="head"
// 2. child.setParent() fires PostAddToViewEvent
// 3. MyFaces HtmlScriptRenderer processes the event
// 3.1. creates javax.faces.Panel for "head"
// 3.2. adds outputScript to it
// 3.3. the parent of outputScript is changed from ViewRoot to "head" Panel component
// 4. outputScript is therefore no longer a child of ViewRoot
List children = new ArrayList(target.getChildren());
for (int i = 0; i < children.size(); i++) {
UIComponent child = (UIComponent) children.get(i);
child.setParent(target);
}
}

View File

@@ -31,8 +31,6 @@ import javax.faces.event.ComponentSystemEventListener;
import javax.faces.event.PhaseId;
import javax.faces.event.SystemEventListener;
import org.springframework.faces.webflow.JsfVersion;
/**
* <p>
* A subclass of AjaxViewRoot for use with JSF 2.0.
@@ -46,13 +44,10 @@ import org.springframework.faces.webflow.JsfVersion;
*
* @author Phil Webb
*/
public class AjaxJsf2ViewRoot extends AjaxViewRoot {
public class Jsf2AjaxViewRoot extends AjaxViewRoot {
public AjaxJsf2ViewRoot(UIViewRoot original) {
public Jsf2AjaxViewRoot(UIViewRoot original) {
super(original);
if (JsfVersion.isAtLeastJsf20()) {
setId(createUniqueId());
}
}
public void addClientBehavior(String eventName, ClientBehavior behavior) {

View File

@@ -0,0 +1,264 @@
/*
* 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 static org.springframework.faces.webflow.JsfRuntimeInformation.isAtLeastJsf20;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.NavigationHandler;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.el.MethodBinding;
import javax.faces.el.PropertyResolver;
import javax.faces.el.ReferenceSyntaxException;
import javax.faces.el.ValueBinding;
import javax.faces.el.VariableResolver;
import javax.faces.event.ActionListener;
import javax.faces.validator.Validator;
import org.springframework.util.Assert;
/**
* Wraps an {@link Application} instance in order to ensure Web Flow specific implementations of {@link ViewHandler} and
* {@link StateManager} are inserted at the front of the processing chain in JSF 1.2 and JSF 2.0 environments. This is
* done by intercepting the corresponding setters. All other methods are simple delegation methods.
*
* @author Rossen Stoyanchev
*
* @see Jsf2FlowApplication
*/
public class FlowApplication extends Application {
private Application delegate;
/**
* Class constructor that accepts a delegate Application instance. If the delegate has default instantiation logic
* for its StateManager and ViewHandler instances, those will be wrapped with {@link FlowViewStateManager} and a
* {@link FlowViewHandler} instance.
*
* @param delegate the Application instance to delegate to.
*/
public FlowApplication(Application delegate) {
Assert.notNull(delegate, "The delegate Application instance must not be null!");
this.delegate = delegate;
ViewHandler handler = this.delegate.getViewHandler();
if (shouldWrap(handler)) {
wrapAndSetViewHandler(handler);
}
StateManager manager = this.delegate.getStateManager();
if (shouldWrap(manager)) {
wrapAndSetStateManager(manager);
}
}
/**
* @return the wrapped Application instance
*/
public Application getDelegate() {
return delegate;
}
/**
* Inserts {@link FlowViewStateManager} in front of the given StateManager (if not already done).
*/
public void setStateManager(StateManager manager) {
if (shouldWrap(manager)) {
wrapAndSetStateManager(manager);
} else {
delegate.setStateManager(manager);
}
}
/**
* Inserts a {@link FlowViewHandler} in front of the given ViewHandler (if not already done).
*/
public void setViewHandler(ViewHandler handler) {
if (shouldWrap(handler)) {
wrapAndSetViewHandler(handler);
} else {
delegate.setViewHandler(handler);
}
}
// ------------------- JSF 1.2 pass-through delegate methods ------------------//
public void addComponent(String componentType, String componentClass) {
delegate.addComponent(componentType, componentClass);
}
public void addConverter(String converterId, String converterClass) {
delegate.addConverter(converterId, converterClass);
}
public void addConverter(Class<?> targetClass, String converterClass) {
delegate.addConverter(targetClass, converterClass);
}
public void addValidator(String validatorId, String validatorClass) {
delegate.addValidator(validatorId, validatorClass);
}
public UIComponent createComponent(String componentType) throws FacesException {
return delegate.createComponent(componentType);
}
public UIComponent createComponent(ValueBinding componentBinding, FacesContext context, String componentType)
throws FacesException {
return delegate.createComponent(componentBinding, context, componentType);
}
public Converter createConverter(String converterId) {
return delegate.createConverter(converterId);
}
public Converter createConverter(Class<?> targetClass) {
return delegate.createConverter(targetClass);
}
public MethodBinding createMethodBinding(String ref, Class<?>[] params) throws ReferenceSyntaxException {
return delegate.createMethodBinding(ref, params);
}
public Validator createValidator(String validatorId) throws FacesException {
return delegate.createValidator(validatorId);
}
public ValueBinding createValueBinding(String ref) throws ReferenceSyntaxException {
return delegate.createValueBinding(ref);
}
public ActionListener getActionListener() {
return delegate.getActionListener();
}
public Iterator<String> getComponentTypes() {
return delegate.getComponentTypes();
}
public Iterator<String> getConverterIds() {
return delegate.getConverterIds();
}
public Iterator<Class<?>> getConverterTypes() {
return delegate.getConverterTypes();
}
public Locale getDefaultLocale() {
return delegate.getDefaultLocale();
}
public String getDefaultRenderKitId() {
return delegate.getDefaultRenderKitId();
}
public String getMessageBundle() {
return delegate.getMessageBundle();
}
public NavigationHandler getNavigationHandler() {
return delegate.getNavigationHandler();
}
public PropertyResolver getPropertyResolver() {
return delegate.getPropertyResolver();
}
public StateManager getStateManager() {
return delegate.getStateManager();
}
public Iterator<Locale> getSupportedLocales() {
return delegate.getSupportedLocales();
}
public Iterator<String> getValidatorIds() {
return delegate.getValidatorIds();
}
public VariableResolver getVariableResolver() {
return delegate.getVariableResolver();
}
public ViewHandler getViewHandler() {
return delegate.getViewHandler();
}
public void setActionListener(ActionListener listener) {
delegate.setActionListener(listener);
}
public void setDefaultLocale(Locale locale) {
delegate.setDefaultLocale(locale);
}
public void setDefaultRenderKitId(String renderKitId) {
delegate.setDefaultRenderKitId(renderKitId);
}
public void setMessageBundle(String bundle) {
delegate.setMessageBundle(bundle);
}
public void setNavigationHandler(NavigationHandler handler) {
delegate.setNavigationHandler(handler);
}
public void setPropertyResolver(PropertyResolver resolver) {
delegate.setPropertyResolver(resolver);
}
public void setSupportedLocales(Collection<Locale> locales) {
delegate.setSupportedLocales(locales);
}
public void setVariableResolver(VariableResolver resolver) {
delegate.setVariableResolver(resolver);
}
// ------------------- Private helper methods ------------------//
private boolean shouldWrap(ViewHandler delegateViewHandler) {
return (delegateViewHandler != null) && (!(delegateViewHandler instanceof FlowViewHandler));
}
private boolean wrapAndSetViewHandler(ViewHandler target) {
if ((target != null) && (!(target instanceof FlowViewHandler))) {
ViewHandler handler = (isAtLeastJsf20()) ? new Jsf2FlowViewHandler(target) : new FlowViewHandler(target);
delegate.setViewHandler(handler);
return true;
}
return false;
}
private boolean shouldWrap(StateManager manager) {
return (manager != null) && (!(manager instanceof FlowViewStateManager));
}
private void wrapAndSetStateManager(StateManager target) {
delegate.setStateManager(new FlowViewStateManager(target));
}
}

View File

@@ -15,33 +15,38 @@
*/
package org.springframework.faces.webflow;
import static org.springframework.faces.webflow.JsfRuntimeInformation.isAtLeastJsf20;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.StateManager;
import org.springframework.util.Assert;
/**
* Custom {@link ApplicationFactory} that ensures the FlowViewStateManager is the first {@link StateManager} in the
* chain so that Web Flow may manage JSF component state when a flow is active.
* Custom {@link ApplicationFactory} that ensures the FlowApplication is the first {@link Application} in the chain,
* which in turn guarantees the install order for other JSF components.
*
* @see FlowApplication
*
* @author Jeremy Grelle
*
*/
public class FlowApplicationFactory extends ApplicationFactory {
private ApplicationFactory delegate;
public FlowApplicationFactory(ApplicationFactory delegate) {
Assert.notNull(delegate, "The delegate ApplicationFactory instance must not be null!");
this.delegate = delegate;
}
public Application getApplication() {
Application app = delegate.getApplication();
// Ensure that FlowViewStateManager is first in the chain
if (app.getStateManager() != null && !(app.getStateManager() instanceof FlowViewStateManager)) {
FlowViewStateManager sm = new FlowViewStateManager(app.getStateManager());
app.setStateManager(sm);
Application delegateApplication = delegate.getApplication();
if (delegateApplication != null && (!(delegateApplication instanceof FlowApplication))) {
Application flowApplication = (isAtLeastJsf20()) ? new Jsf2FlowApplication(delegateApplication)
: new FlowApplication(delegateApplication);
setApplication(flowApplication);
}
return app;
return delegate.getApplication();
}
public void setApplication(Application application) {

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.faces.webflow;
import static org.springframework.faces.webflow.JsfRuntimeInformation.isAtLeastJsf20;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.context.FacesContext;
@@ -62,6 +64,9 @@ public class FlowLifecycle extends Lifecycle {
for (int p = PhaseId.APPLY_REQUEST_VALUES.getOrdinal(); p <= PhaseId.INVOKE_APPLICATION.getOrdinal(); p++) {
PhaseId phaseId = (PhaseId) PhaseId.VALUES.get(p);
if (!skipPhase(context, phaseId)) {
if (isAtLeastJsf20()) {
context.setCurrentPhaseId(phaseId);
}
invokePhase(context, phaseId);
}
}

View File

@@ -1,19 +1,34 @@
/*
* 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 java.net.URL;
import javax.faces.FacesException;
import javax.faces.view.facelets.ResourceResolver;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.RequestContextHolder;
import com.sun.facelets.impl.DefaultResourceResolver;
import com.sun.facelets.impl.ResourceResolver;
import com.sun.faces.facelets.impl.DefaultResourceResolver;
public class FlowResourceResolver implements ResourceResolver {
public class FlowResourceResolver extends ResourceResolver {
ResourceResolver delegateResolver = new DefaultResourceResolver();

View File

@@ -26,6 +26,7 @@ import javax.faces.context.FacesContext;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ContextResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.RequestContextHolder;
import org.springframework.webflow.execution.View;
@@ -35,15 +36,22 @@ import org.springframework.webflow.execution.View;
* including the current FlowExecutionKey, so that postbacks may be properly intercepted and handled by Web Flow.
*
* @author Jeremy Grelle
*
* @see Jsf2FlowViewHandler
*/
public class FlowViewHandler extends ViewHandler {
private ViewHandler delegate;
public FlowViewHandler(ViewHandler delegate) {
Assert.notNull(delegate, "The delegate ViewHandler instance must not be null!");
this.delegate = delegate;
}
protected ViewHandler getDelegate() {
return delegate;
}
public String getActionURL(FacesContext context, String viewId) {
if (JsfUtils.isFlowRequest()) {
return RequestContextHolder.getRequestContext().getFlowExecutionUrl();
@@ -95,6 +103,14 @@ public class FlowViewHandler extends ViewHandler {
delegate.writeState(context);
}
public String deriveViewId(FacesContext context, String rawViewId) {
if (JsfUtils.isFlowRequest()) {
return resolveResourcePath(RequestContextHolder.getRequestContext(), rawViewId);
} else {
return getDelegate().deriveViewId(context, rawViewId);
}
}
// --------------------- Private Helpers ------------------------------//
private String resolveResourcePath(RequestContext context, String viewId) {

View File

@@ -103,10 +103,21 @@ public class FlowViewStateManager extends StateManager {
public void writeState(FacesContext context, javax.faces.application.StateManager.SerializedView state)
throws IOException {
// Ensures that javax.faces.ViewState hidden field always gets written - needed for third-party component
// compatability
// compatibility
delegate.writeState(context, state);
}
public void writeState(FacesContext context, Object state) throws IOException {
if (state instanceof Object[]) {
delegate.writeState(context, state); // MyFaces
} else if (state instanceof FlowSerializedView) { // Mojarra
FlowSerializedView view = (FlowSerializedView) state;
delegate.writeState(context, new Object[] { view.getTreeStructure(), view.getComponentState() });
} else {
super.writeState(context, state);
}
}
public boolean isSavingStateInClient(FacesContext context) {
if (!JsfUtils.isFlowRequest()) {
return delegate.isSavingStateInClient(context);
@@ -161,4 +172,5 @@ public class FlowViewStateManager extends StateManager {
}
return viewRoot;
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import javax.el.ELContextListener;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.ProjectStage;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.behavior.Behavior;
import javax.faces.context.FacesContext;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
/**
* Extends FlowApplication 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.
*
* @author Rossen Stoyanchev
*/
public class Jsf2FlowApplication extends FlowApplication {
public Jsf2FlowApplication(Application delegate) {
super(delegate);
}
// ------------------- JSF 2 pass-through delegate methods ------------------//
public void addBehavior(String behaviorId, String behaviorClass) {
getDelegate().addBehavior(behaviorId, behaviorClass);
}
public void addDefaultValidatorId(String validatorId) {
getDelegate().addDefaultValidatorId(validatorId);
}
public void addELContextListener(ELContextListener listener) {
getDelegate().addELContextListener(listener);
}
public void addELResolver(ELResolver resolver) {
getDelegate().addELResolver(resolver);
}
public Behavior createBehavior(String behaviorId) throws FacesException {
return getDelegate().createBehavior(behaviorId);
}
public UIComponent createComponent(FacesContext context, Resource componentResource) {
return getDelegate().createComponent(context, componentResource);
}
public UIComponent createComponent(FacesContext context, String componentType, String rendererType) {
return getDelegate().createComponent(context, componentType, rendererType);
}
public UIComponent createComponent(ValueExpression componentExpression, FacesContext context, String componentType,
String rendererType) {
return getDelegate().createComponent(componentExpression, context, componentType, rendererType);
}
public UIComponent createComponent(ValueExpression componentExpression, FacesContext context, String componentType)
throws FacesException {
return getDelegate().createComponent(componentExpression, context, componentType);
}
public <T> T evaluateExpressionGet(FacesContext context, String expression, Class<? extends T> expectedType)
throws ELException {
return getDelegate().evaluateExpressionGet(context, expression, expectedType);
}
public Iterator<String> getBehaviorIds() {
return getDelegate().getBehaviorIds();
}
public Map<String, String> getDefaultValidatorInfo() {
return getDelegate().getDefaultValidatorInfo();
}
public ELContextListener[] getELContextListeners() {
return getDelegate().getELContextListeners();
}
public ELResolver getELResolver() {
return getDelegate().getELResolver();
}
public ExpressionFactory getExpressionFactory() {
return getDelegate().getExpressionFactory();
}
public ProjectStage getProjectStage() {
return getDelegate().getProjectStage();
}
public ResourceBundle getResourceBundle(FacesContext ctx, String name) {
return getDelegate().getResourceBundle(ctx, name);
}
public ResourceHandler getResourceHandler() {
return getDelegate().getResourceHandler();
}
public void publishEvent(FacesContext context, Class<? extends SystemEvent> systemEventClass,
Class<?> sourceBaseType, Object source) {
getDelegate().publishEvent(context, systemEventClass, sourceBaseType, source);
}
public void publishEvent(FacesContext context, Class<? extends SystemEvent> systemEventClass, Object source) {
getDelegate().publishEvent(context, systemEventClass, source);
}
public void removeELContextListener(ELContextListener listener) {
getDelegate().removeELContextListener(listener);
}
public void setResourceHandler(ResourceHandler resourceHandler) {
getDelegate().setResourceHandler(resourceHandler);
}
public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass,
SystemEventListener listener) {
getDelegate().subscribeToEvent(systemEventClass, sourceClass, listener);
}
public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener) {
getDelegate().subscribeToEvent(systemEventClass, listener);
}
public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, Class<?> sourceClass,
SystemEventListener listener) {
getDelegate().unsubscribeFromEvent(systemEventClass, sourceClass, listener);
}
public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass, SystemEventListener listener) {
getDelegate().unsubscribeFromEvent(systemEventClass, listener);
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.util.Map;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewDeclarationLanguage;
/**
* Extends FlowViewHandler 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.
*
* @author Rossen Stoyanchev
*/
public class Jsf2FlowViewHandler extends FlowViewHandler {
public Jsf2FlowViewHandler(ViewHandler delegate) {
super(delegate);
}
// --------------- JSF 2.0 Pass-through delegate methods ------------------//
public String calculateCharacterEncoding(FacesContext context) {
return getDelegate().calculateCharacterEncoding(context);
}
public String getBookmarkableURL(FacesContext context, String viewId, Map parameters, boolean includeViewParams) {
return getDelegate().getBookmarkableURL(context, viewId, parameters, includeViewParams);
}
public String getRedirectURL(FacesContext context, String viewId, Map parameters, boolean includeViewParams) {
return getDelegate().getRedirectURL(context, viewId, parameters, includeViewParams);
}
public ViewDeclarationLanguage getViewDeclarationLanguage(FacesContext context, String viewId) {
return getDelegate().getViewDeclarationLanguage(context, viewId);
}
public void initView(FacesContext context) throws FacesException {
getDelegate().initView(context);
}
}

View File

@@ -17,46 +17,40 @@ package org.springframework.faces.webflow;
import javax.faces.context.FacesContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Internal helper class to find the version of JSF in use at runtime.
* Helper class to provide information about the JSF runtime environment such as JSF version and implementation.
*
* @author Phil Webb
*/
public class JsfVersion {
public class JsfRuntimeInformation {
/**
* JSF Version 1.1
*/
/** JSF Version 1.1 */
public static final int JSF_11 = 0;
/**
* JSF Version 1.2
*/
/** JSF Version 1.2 */
public static final int JSF_12 = 1;
/**
* JSF Version 2.0
*/
/** JSF Version 2.0 */
public static final int JSF_20 = 2;
private static final int jsfVersion;
private static final boolean myFacesPresent = ClassUtils.isPresent("org.apache.myfaces.webapp.MyFacesServlet",
JsfUtils.class.getClassLoader());
static {
if (ReflectionUtils.findMethod(FacesContext.class, "isPostback") != null) {
jsfVersion = JsfVersion.JSF_20;
jsfVersion = JSF_20;
} else if (ReflectionUtils.findMethod(FacesContext.class, "getELContext") != null) {
jsfVersion = JsfVersion.JSF_12;
jsfVersion = JSF_12;
} else {
jsfVersion = JsfVersion.JSF_11;
jsfVersion = JSF_11;
}
}
public static int getJsfVersion() {
return jsfVersion;
}
public static boolean isAtLeastJsf20() {
return jsfVersion >= JSF_20;
}
@@ -65,4 +59,12 @@ public class JsfVersion {
return jsfVersion >= JSF_12;
}
protected static boolean isMyFacesPresent() {
return myFacesPresent;
}
public static boolean isPortletRequest(FacesContext context) {
return context.getExternalContext().getContext().getClass().getName().indexOf("Portlet") != -1;
}
}

View File

@@ -15,12 +15,13 @@
*/
package org.springframework.faces.webflow;
import static org.springframework.faces.webflow.JsfRuntimeInformation.isAtLeastJsf12;
import java.io.IOException;
import java.io.Serializable;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
import javax.faces.lifecycle.Lifecycle;
import org.apache.commons.logging.Log;
@@ -84,10 +85,8 @@ public class JsfView implements View {
}
facesContext.setViewRoot(viewRoot);
try {
JsfUtils.notifyBeforeListeners(PhaseId.RENDER_RESPONSE, facesLifecycle, facesContext);
logger.debug("Asking view handler to render view");
facesContext.getApplication().getViewHandler().renderView(facesContext, viewRoot);
JsfUtils.notifyAfterListeners(PhaseId.RENDER_RESPONSE, facesLifecycle, facesContext);
logger.debug("Asking faces lifecycle to render");
facesLifecycle.render(facesContext);
} finally {
logger.debug("View rendering complete");
facesContext.responseComplete();
@@ -96,7 +95,7 @@ public class JsfView implements View {
}
public boolean userEventQueued() {
if (JsfUtils.isAtLeastJsf12()) {
if (isAtLeastJsf12()) {
return requestContext.getRequestParameters().contains("javax.faces.ViewState");
} else {
return requestContext.getRequestParameters().size() > 1;

View File

@@ -15,6 +15,10 @@
*/
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.isPortletRequest;
import java.util.Iterator;
import javax.faces.application.ViewHandler;
@@ -32,8 +36,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.binding.expression.Expression;
import org.springframework.faces.ui.AjaxJsf2ViewRoot;
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;
@@ -70,13 +74,16 @@ public class JsfViewFactory implements ViewFactory {
public View getView(RequestContext context) {
FacesContext facesContext = FlowFacesContext.newInstance(context, lifecycle);
try {
if (isAtLeastJsf20()) {
facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW);
}
if (!facesContext.getRenderResponse()) {
// only publish a RESTORE_VIEW event if this is the first phase of the lifecycle
// this won't be true when this method is called after a transition from one view-state to another
JsfUtils.notifyBeforeListeners(PhaseId.RESTORE_VIEW, lifecycle, facesContext);
}
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
if (JsfUtils.isAtLeastJsf12() && !JsfUtils.isPortlet(facesContext)) {
if (isAtLeastJsf12() && (!isPortletRequest(facesContext))) {
viewHandler.initView(facesContext);
}
JsfView view;
@@ -137,7 +144,7 @@ public class JsfViewFactory implements ViewFactory {
private JsfView createJsfView(UIViewRoot root, Lifecycle lifecycle, RequestContext context) {
if (isSpringJavascriptAjaxRequest(context.getExternalContext())) {
AjaxViewRoot viewRoot = (JsfVersion.isAtLeastJsf20()) ? new AjaxJsf2ViewRoot(root) : new AjaxViewRoot(root);
AjaxViewRoot viewRoot = (isAtLeastJsf20()) ? new Jsf2AjaxViewRoot(root) : new AjaxViewRoot(root);
return new JsfView(viewRoot, lifecycle, context);
} else {
return new JsfView(root, lifecycle, context);

View File

@@ -11,9 +11,8 @@
<variable-resolver>org.springframework.faces.webflow.FlowVariableResolver</variable-resolver>
<property-resolver>org.springframework.faces.webflow.FlowPropertyResolver</property-resolver>
<variable-resolver>org.springframework.faces.webflow.SpringBeanWebFlowVariableResolver</variable-resolver>
<view-handler>org.springframework.faces.webflow.FlowViewHandler</view-handler>
</application>
<factory>
<application-factory>org.springframework.faces.webflow.FlowApplicationFactory</application-factory>
</factory>

View File

@@ -8,16 +8,18 @@ import javax.faces.component.UIColumn;
import javax.faces.component.UICommand;
import javax.faces.component.UIData;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import junit.framework.TestCase;
import org.apache.myfaces.test.mock.MockFacesContext;
import org.springframework.faces.webflow.JSFMockHelper;
import org.springframework.util.ReflectionUtils;
import com.sun.facelets.component.UIRepeat;
import com.sun.faces.facelets.component.UIRepeat;
public class SelectionTrackingActionListenerTests extends TestCase {
@@ -92,10 +94,11 @@ public class SelectionTrackingActionListenerTests extends TestCase {
uiRepeat.getChildren().add(commandButton);
viewToTest.getChildren().add(uiRepeat);
Method indexMutator = ReflectionUtils.findMethod(UIRepeat.class, "setIndex", new Class[] { int.class });
Method indexMutator = ReflectionUtils.findMethod(UIRepeat.class, "setIndex", new Class[] { FacesContext.class,
int.class });
indexMutator.setAccessible(true);
ReflectionUtils.invokeMethod(indexMutator, uiRepeat, new Object[] { new Integer(1) });
ReflectionUtils.invokeMethod(indexMutator, uiRepeat, new Object[] { new MockFacesContext(), new Integer(1) });
ActionEvent event = new ActionEvent(commandButton);
@@ -105,7 +108,7 @@ public class SelectionTrackingActionListenerTests extends TestCase {
assertSame(dataModel.getSelectedRow(), dataModel.getRowData());
assertTrue(delegateListener.processedEvent);
ReflectionUtils.invokeMethod(indexMutator, uiRepeat, new Object[] { new Integer(2) });
ReflectionUtils.invokeMethod(indexMutator, uiRepeat, new Object[] { new MockFacesContext(), new Integer(2) });
assertFalse(dataModel.isCurrentRowSelected());
assertTrue(dataModel.getSelectedRow() != dataModel.getRowData());
}

View File

@@ -15,5 +15,6 @@ Import-Template:
javax.servlet.http;version="[2.4.0, 3.0.0)",
javax.faces.*;version="[1.2.0, 3.0.0)",
org.ajax4jsf.*;version="[1.1.1, 2.0.0)";resolution:=optional,
com.sun.faces.*;version="[2.0.0, 3.0.0)";resolution:=optional,
com.sun.facelets.*;version="[1.1.0, 2.0.0)";resolution:=optional,
org.w3c.dom;version="0"

View File

@@ -29,6 +29,7 @@ import java.io.ObjectStreamClass;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@@ -229,6 +230,20 @@ public class SerializedFlowExecutionSnapshot extends FlowExecutionSnapshot imple
private static class ConfigurableObjectInputStream extends ObjectInputStream {
/* Temporary workaround for SPR-???? */
private static final HashMap PRIMITIVE_CLASSES = new HashMap(8, 1.0F);
static {
PRIMITIVE_CLASSES.put("boolean", boolean.class);
PRIMITIVE_CLASSES.put("byte", byte.class);
PRIMITIVE_CLASSES.put("char", char.class);
PRIMITIVE_CLASSES.put("short", short.class);
PRIMITIVE_CLASSES.put("int", int.class);
PRIMITIVE_CLASSES.put("long", long.class);
PRIMITIVE_CLASSES.put("float", float.class);
PRIMITIVE_CLASSES.put("double", double.class);
PRIMITIVE_CLASSES.put("void", void.class);
}
private final ClassLoader classLoader;
public ConfigurableObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException {
@@ -237,7 +252,16 @@ public class SerializedFlowExecutionSnapshot extends FlowExecutionSnapshot imple
}
protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
return ClassUtils.forName(desc.getName(), classLoader);
String name = desc.getName();
try {
return ClassUtils.forName(desc.getName(), classLoader);
} catch (ClassNotFoundException ex) {
Class rtn = (Class) PRIMITIVE_CLASSES.get(name);
if (rtn == null) {
throw ex;
}
return rtn;
}
}
protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {