@@ -38,18 +41,35 @@
-
-
-
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
-
+
+
+
+
+
diff --git a/spring-faces/src/main/java/org/springframework/faces/mvc/JsfView.java b/spring-faces/src/main/java/org/springframework/faces/mvc/JsfView.java
index cbe52e26..e88c055e 100644
--- a/spring-faces/src/main/java/org/springframework/faces/mvc/JsfView.java
+++ b/spring-faces/src/main/java/org/springframework/faces/mvc/JsfView.java
@@ -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();
diff --git a/spring-faces/src/main/java/org/springframework/faces/ui/AjaxViewRoot.java b/spring-faces/src/main/java/org/springframework/faces/ui/AjaxViewRoot.java
index 9be0777d..53b57c55 100644
--- a/spring-faces/src/main/java/org/springframework/faces/ui/AjaxViewRoot.java
+++ b/spring-faces/src/main/java/org/springframework/faces/ui/AjaxViewRoot.java
@@ -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);
}
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/ui/AjaxJsf2ViewRoot.java b/spring-faces/src/main/java/org/springframework/faces/ui/Jsf2AjaxViewRoot.java
similarity index 96%
rename from spring-faces/src/main/java/org/springframework/faces/ui/AjaxJsf2ViewRoot.java
rename to spring-faces/src/main/java/org/springframework/faces/ui/Jsf2AjaxViewRoot.java
index db5bab97..43531136 100644
--- a/spring-faces/src/main/java/org/springframework/faces/ui/AjaxJsf2ViewRoot.java
+++ b/spring-faces/src/main/java/org/springframework/faces/ui/Jsf2AjaxViewRoot.java
@@ -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;
-
/**
*
* 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) {
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowApplication.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowApplication.java
new file mode 100644
index 00000000..c05a4967
--- /dev/null
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowApplication.java
@@ -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 getComponentTypes() {
+ return delegate.getComponentTypes();
+ }
+
+ public Iterator getConverterIds() {
+ return delegate.getConverterIds();
+ }
+
+ public Iterator> 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 getSupportedLocales() {
+ return delegate.getSupportedLocales();
+ }
+
+ public Iterator 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 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));
+ }
+
+}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowApplicationFactory.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowApplicationFactory.java
index 3d99f735..a03db806 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowApplicationFactory.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowApplicationFactory.java
@@ -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) {
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowLifecycle.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowLifecycle.java
index f11825b3..2f652307 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowLifecycle.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowLifecycle.java
@@ -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);
}
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResourceResolver.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResourceResolver.java
index e610a309..c949fd01 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResourceResolver.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResourceResolver.java
@@ -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();
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewHandler.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewHandler.java
index e93637f1..8cbbff81 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewHandler.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewHandler.java
@@ -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) {
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java
index bc7b7875..d7326b8b 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java
@@ -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;
}
+
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowApplication.java b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowApplication.java
new file mode 100644
index 00000000..115cf4e7
--- /dev/null
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowApplication.java
@@ -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 evaluateExpressionGet(FacesContext context, String expression, Class extends T> expectedType)
+ throws ELException {
+ return getDelegate().evaluateExpressionGet(context, expression, expectedType);
+ }
+
+ public Iterator getBehaviorIds() {
+ return getDelegate().getBehaviorIds();
+ }
+
+ public Map 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);
+ }
+
+}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowViewHandler.java b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowViewHandler.java
new file mode 100644
index 00000000..a0099921
--- /dev/null
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowViewHandler.java
@@ -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);
+ }
+
+}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfVersion.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfRuntimeInformation.java
similarity index 64%
rename from spring-faces/src/main/java/org/springframework/faces/webflow/JsfVersion.java
rename to spring-faces/src/main/java/org/springframework/faces/webflow/JsfRuntimeInformation.java
index 85e7c65e..933ea91b 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfVersion.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfRuntimeInformation.java
@@ -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;
+ }
+
}
\ No newline at end of file
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java
index 0f92ea74..d9643d43 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java
@@ -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;
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java
index 44e62be7..9801fb69 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java
@@ -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);
diff --git a/spring-faces/src/main/resources/META-INF/faces-config.xml b/spring-faces/src/main/resources/META-INF/faces-config.xml
index b08197b6..0ef11f11 100644
--- a/spring-faces/src/main/resources/META-INF/faces-config.xml
+++ b/spring-faces/src/main/resources/META-INF/faces-config.xml
@@ -11,9 +11,8 @@
org.springframework.faces.webflow.FlowVariableResolver
org.springframework.faces.webflow.FlowPropertyResolver
org.springframework.faces.webflow.SpringBeanWebFlowVariableResolver
- org.springframework.faces.webflow.FlowViewHandler
-
+
org.springframework.faces.webflow.FlowApplicationFactory
diff --git a/spring-faces/src/test/java/org/springframework/faces/model/SelectionTrackingActionListenerTests.java b/spring-faces/src/test/java/org/springframework/faces/model/SelectionTrackingActionListenerTests.java
index 07ff011f..1a677241 100644
--- a/spring-faces/src/test/java/org/springframework/faces/model/SelectionTrackingActionListenerTests.java
+++ b/spring-faces/src/test/java/org/springframework/faces/model/SelectionTrackingActionListenerTests.java
@@ -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());
}
diff --git a/spring-faces/template.mf b/spring-faces/template.mf
index 76ac0df9..0ba14caa 100644
--- a/spring-faces/template.mf
+++ b/spring-faces/template.mf
@@ -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"
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/snapshot/SerializedFlowExecutionSnapshot.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/snapshot/SerializedFlowExecutionSnapshot.java
index f797f1d5..0b5114e6 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/snapshot/SerializedFlowExecutionSnapshot.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/snapshot/SerializedFlowExecutionSnapshot.java
@@ -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 {