dev@javaserverfaces.java.net

Re: Seeking Review: 8-ViewLifecycle

From: Roger Kitain <Roger.Kitain_at_Sun.COM>
Date: Thu, 11 Nov 2004 12:45:00 -0500

Just a few comments... (inline, of course)..
also, take into accout Adam's Comments.

-roger

Ed Burns wrote:

>Issue: 8-ViewLifecycle
>
>PENDING(edburns): complete the RI for this.
>
>SECTION: API Changes
>
>M jsf-api/src/javax/faces/component/UIViewRoot.java
>
>- add the following JavaBeans properties
>
> MethodBinding beforePhaseListener
>
> MethodBinding afterPhaseListener
>
> MethodBinding afterPhaseListener
>
NIT - you mentioned this twice.
NIT - The properties you mention here are "afterPhaseListener"/
"beforePhaseListener" - which correspond to your javaBean
method name. but below, you use "beforehase", "afterPhase".

>
>- add the following JavaBeans listener
>
> PhaseListener
>
>- modify encode{Begin,End}() to invoke the listeners as per spec.
>
>M jsf-api/test/javax/faces/component/UIViewRootTestCase.java
>
>- test the MethodBinding JavaBeans property, and PhaseListener JavaBeans
> listener, individually, and together, and with and without state saving.
>
>M jsf-api/test/javax/faces/mock/MockExternalContext.java
>
>- avoid throwing UnsupportedOperationException() when asked for the
> initParameter for the lifecycleid. Return null instead.
>
>SECTION: API Diffs
>
>Index: jsf-api/src/javax/faces/component/UIViewRoot.java
>===================================================================
>RCS file: /cvs/javaserverfaces-sources/jsf-api/src/javax/faces/component/UIViewRoot.java,v
>retrieving revision 1.28
>diff -u -r1.28 UIViewRoot.java
>--- jsf-api/src/javax/faces/component/UIViewRoot.java 7 Apr 2004 17:36:58 -0000 1.28
>+++ jsf-api/src/javax/faces/component/UIViewRoot.java 11 Nov 2004 16:07:24 -0000
>@@ -15,13 +15,21 @@
> import java.util.Iterator;
> import java.util.List;
> import java.util.Locale;
>+import javax.faces.FactoryFinder;
>+import javax.faces.FacesException;
>+import javax.faces.lifecycle.LifecycleFactory;
>+import javax.faces.lifecycle.Lifecycle;
> import javax.faces.context.FacesContext;
> import javax.faces.event.AbortProcessingException;
> import javax.faces.event.FacesEvent;
> import javax.faces.event.PhaseId;
>+import javax.faces.event.PhaseEvent;
>+import javax.faces.event.PhaseListener;
> import javax.faces.render.RenderKit;
> import javax.faces.render.RenderKitFactory;
> import javax.faces.el.ValueBinding;
>+import javax.faces.el.MethodBinding;
>+import javax.faces.webapp.FacesServlet;
>
>
> /**
>@@ -170,6 +178,63 @@
>
> // ------------------------------------------------ Event Management Methods
>
>+ private MethodBinding beforePhase = null;
>+ private MethodBinding afterPhase = null;
>
make these "beforePhaseListener"/"afterPhaseListener"?

>+
>+ /**
>+ * @return the {_at_link MethodBinding} that will be invoked before
>+ * this view is rendered.
>+ *
>+ */
>+
>+ public MethodBinding getBeforePhaseListener() {
>+ return beforePhase;
>+ }
>
make this "return beforePhaseListener"
similar changes below

>+
>+ /**
>+ * @param newBeforePhase the {_at_link MethodBinding} that will be
>+ * invoked before this view is rendered.
>+ *
>+ */
>+
>+ public void setBeforePhaseListener(MethodBinding newBeforePhase) {
>+ beforePhase = newBeforePhase;
>+ }
>+
>+ /**
>+ * @return the {_at_link MethodBinding} that will be invoked after
>+ * this view is rendered.
>+ *
>+ */
>+
>+ public MethodBinding getAfterPhaseListener() {
>+ return afterPhase;
>+ }
>+
>+ /**
>+ * @param newAfterPhase the {_at_link MethodBinding} that will be
>+ * invoked after this view is rendered.
>+ *
>+ */
>+
>+ public void setAfterPhaseListener(MethodBinding newAfterPhase) {
>+ afterPhase = newAfterPhase;
>+ }
>+
>+ private List phaseListeners = null;
>+
>+ public void removePhaseListener(PhaseListener toRemove) {
>+ if (null != phaseListeners) {
>+ phaseListeners.remove(toRemove);
>+ }
>+ }
>+
>+ public void addPhaseListener(PhaseListener newPhaseListener) {
>+ if (null == phaseListeners) {
>+ phaseListeners = new ArrayList();
>+ }
>+ phaseListeners.add(newPhaseListener);
>+ }
>
> /**
> * <p>An array of Lists of events that have been queued for later
>@@ -314,16 +379,112 @@
>
> /**
> * <p>Override the default {_at_link UIComponentBase#encodeBegin}
>- * behavior to reset the mechanism used in {_at_link #createUniqueId}
>- * before falling through to the standard superclass processing.</p>
>+ * behavior. Reset the mechanism used in {_at_link #createUniqueId}
>+ * before falling through to the standard superclass processing. If
>+ * {_at_link #getBeforePhaseListener} returns non-<code>null</code>,
>+ * invoke it, passing a {_at_link PhaseEvent} for the {_at_link
>+ * PhaseId.RENDER_RESPONSE} phase. If the internal list populated
>+ * by calls to {_at_link #addPhaseListener} is non-empty, any listeners
>+ * in that list must have their {_at_link PhaseListener#beforePhase}
>+ * method called, passing the <code>PhaseEvent</code>. Any errors
>+ * that occur during invocation of any of the the beforePhase
>+ * listeners must be logged and swallowed.</p>
> *
> */
>
> public void encodeBegin(FacesContext context) throws IOException {
> lastId = 0;
>+
>+ // avoid creating the PhaseEvent if possible by doing redundant
>+ // null checks.
>+ if (null != beforePhase || null != phaseListeners) {
>+ PhaseEvent event = createPhaseEvent(context);
>+ if (null != beforePhase) {
>+ try {
>+ beforePhase.invoke(context,
>+ new Object [] { event });
>+ }
>+ catch (Exception e) {
>+ // PENDING(edburns): log this
>+ }
>+ }
>+ if (null != phaseListeners) {
>+ Iterator iter = phaseListeners.iterator();
>+ PhaseListener curListener = null;
>+ while (iter.hasNext()) {
>+ curListener = (PhaseListener) iter.next();
>+ try {
>+ curListener.beforePhase(event);
>+ }
>+ catch (Exception e) {
>+ // PENDING(edburns): log this
>+ }
>+ }
>+ }
>+ }
>+
> super.encodeBegin(context);
> }
>
>+ /**
>+ * <p>Override the default {_at_link UIComponentBase#encodeEnd}
>+ * behavior. If {_at_link #getAfterPhaseListener} returns
>+ * non-<code>null</code>, invoke it, passing a {_at_link PhaseEvent}
>+ * for the {_at_link PhaseId.RENDER_RESPONSE} phase. Any errors that
>+ * occur during invocation of the afterPhase listener must be
>+ * logged and swallowed.</p>
>+ */
>+
>+ public void encodeEnd(FacesContext context) throws IOException {
>+ super.encodeEnd(context);
>+
>+ // avoid creating the PhaseEvent if possible by doing redundant
>+ // null checks.
>+ if (null != afterPhase || null != phaseListeners) {
>+ PhaseEvent event = createPhaseEvent(context);
>+ if (null != afterPhase) {
>+ try {
>+ afterPhase.invoke(context,
>+ new Object [] { event });
>+ }
>+ catch (Exception e) {
>+ // PENDING(edburns): log this
>+ }
>+ }
>+ if (null != phaseListeners) {
>+ Iterator iter = phaseListeners.iterator();
>+ PhaseListener curListener = null;
>+ while (iter.hasNext()) {
>+ curListener = (PhaseListener) iter.next();
>+ try {
>+ curListener.afterPhase(event);
>+ }
>+ catch (Exception e) {
>+ // PENDING(edburns): log this
>+ }
>+ }
>+ }
>+
>+ }
>+
>+ }
>+
>+ private PhaseEvent createPhaseEvent(FacesContext context) throws FacesException {
>+ Lifecycle lifecycle = null;
>+ LifecycleFactory lifecycleFactory = (LifecycleFactory)
>+ FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
>+ String lifecycleId =
>+ context.getExternalContext().getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
>+ if (lifecycleId == null) {
>+ lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
>+ }
>+ lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
>+
>+ PhaseEvent result = new PhaseEvent(context, PhaseId.RENDER_RESPONSE,
>+ lifecycle);
>+ return result;
>+ }
>+
>
> /**
> * <p>Override the default {_at_link UIComponentBase#processValidators}
>@@ -493,11 +654,14 @@
>
> public Object saveState(FacesContext context) {
>
>- Object values[] = new Object[4];
>+ Object values[] = new Object[7];
> values[0] = super.saveState(context);
> values[1] = renderKitId;
> values[2] = viewId;
> values[3] = locale;
>+ values[4] = saveAttachedState(context, beforePhase);
>+ values[5] = saveAttachedState(context, afterPhase);
>+ values[6] = saveAttachedState(context, phaseListeners);
> return (values);
>
> }
>@@ -510,6 +674,9 @@
> renderKitId = (String) values[1];
> viewId = (String) values[2];
> locale = (Locale)values[3];
>+ beforePhase = (MethodBinding) restoreAttachedState(context, values[4]);
>+ afterPhase = (MethodBinding) restoreAttachedState(context, values[5]);
>+ phaseListeners = (List) restoreAttachedState(context, values[6]);
>
> }
>
>Index: jsf-api/test/javax/faces/component/UIViewRootTestCase.java
>===================================================================
>RCS file: /cvs/javaserverfaces-sources/jsf-api/test/javax/faces/component/UIViewRootTestCase.java,v
>retrieving revision 1.15
>diff -u -r1.15 UIViewRootTestCase.java
>--- jsf-api/test/javax/faces/component/UIViewRootTestCase.java 7 Apr 2004 17:39:27 -0000 1.15
>+++ jsf-api/test/javax/faces/component/UIViewRootTestCase.java 11 Nov 2004 16:07:25 -0000
>@@ -14,14 +14,19 @@
> import java.util.Iterator;
> import java.util.List;
> import java.util.Locale;
>+import javax.faces.FactoryFinder;
> import javax.faces.event.AbortProcessingException;
> import javax.faces.event.FacesEvent;
>+import javax.faces.event.PhaseEvent;
>+import javax.faces.event.PhaseListener;
>+import javax.faces.event.PhaseId;
> import javax.faces.validator.Validator;
> import javax.faces.context.FacesContext;
> import javax.faces.component.UIComponent;
> import javax.faces.component.UIViewRoot;
> import javax.faces.event.PhaseId;
> import javax.faces.el.ValueBinding;
>+import javax.faces.el.MethodBinding;
> import junit.framework.TestCase;
> import junit.framework.Test;
> import junit.framework.TestSuite;
>@@ -67,6 +72,39 @@
>
> }
>
>+ public static String FACTORIES[][] = {
>+ { FactoryFinder.APPLICATION_FACTORY,
>+ "javax.faces.mock.MockApplicationFactory"
>+ },
>+ { FactoryFinder.FACES_CONTEXT_FACTORY,
>+ "javax.faces.mock.MockFacesContextFactory"
>+ },
>+ { FactoryFinder.LIFECYCLE_FACTORY,
>+ "javax.faces.mock.MockLifecycleFactory"
>+ },
>+ { FactoryFinder.RENDER_KIT_FACTORY,
>+ "javax.faces.mock.MockRenderKitFactory"
>+ }
>+ };
>+
>+ public void setUp() {
>+ super.setUp();
>+ for (int i = 0, len = FACTORIES.length; i < len; i++) {
>+ System.getProperties().remove(FACTORIES[i][0]);
>+ }
>+
>+ FactoryFinder.releaseFactories();
>+ int len, i = 0;
>+
>+ // simulate the "faces implementation specific" part
>+ for (i = 0, len = FACTORIES.length; i < len; i++) {
>+ FactoryFinder.setFactory(FACTORIES[i][0],
>+ FACTORIES[i][1]);
>+ }
>+
>+
>+ }
>+
> /**
> * Tear down instance variables required by this test case.
> */
>@@ -167,6 +205,103 @@
>
> }
>
>+ public void testPhaseMethBinding() throws Exception {
>+ UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ doTestPhaseMethodBinding(root);
>+ }
>+
>+ public void testPhaseListener() throws Exception {
>+ UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ doTestPhaseListener(root);
>+ }
>+
>+ public void testPhaseMethodBindingAndListener() throws Exception {
>+ UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ doTestPhaseMethodBindingAndListener(root);
>+ }
>+
>+
>+ public void testPhaseMethBindingState() throws Exception {
>+ UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ Object state = root.saveState(facesContext);
>+ root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ root.restoreState(facesContext, state);
>+
>+ doTestPhaseMethodBinding(root);
>+ }
>+
>+ public void testPhaseListenerState() throws Exception {
>+ UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ Object state = root.saveState(facesContext);
>+ root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ root.restoreState(facesContext, state);
>+
>+ doTestPhaseListener(root);
>+ }
>+
>+ public void testPhaseMethodBindingAndListenerState() throws Exception {
>+ UIViewRoot root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ Object state = root.saveState(facesContext);
>+ root = facesContext.getApplication().getViewHandler().createView(facesContext, null);
>+ root.restoreState(facesContext, state);
>+
>+ doTestPhaseMethodBindingAndListener(root);
>+ }
>+
>+
>+
>+ public void doTestPhaseMethodBinding(UIViewRoot root) throws Exception {
>+ PhaseListenerBean phaseListenerBean = new PhaseListenerBean();
>+ facesContext.getExternalContext().getRequestMap().put("bean",
>+ phaseListenerBean);
>+ Class [] args = new Class [] { PhaseEvent.class };
>+ MethodBinding
>+ beforeBinding = facesContext.getApplication().createMethodBinding("#{bean.beforePhase}", args),
>+ afterBinding = facesContext.getApplication().createMethodBinding("#{bean.afterPhase}", args);
>+ root.setBeforePhaseListener(beforeBinding);
>+ root.setAfterPhaseListener(afterBinding);
>+ root.encodeBegin(facesContext);
>+ root.encodeEnd(facesContext);
>+ assertTrue(phaseListenerBean.isBeforePhaseCalled());
>+ assertTrue(phaseListenerBean.isAfterPhaseCalled());
>+
>+
>+ }
>+
>+ public void doTestPhaseListener(UIViewRoot root) throws Exception {
>+ PhaseListenerBean phaseListener = new PhaseListenerBean();
>+ root.addPhaseListener(phaseListener);
>+ root.encodeBegin(facesContext);
>+ root.encodeEnd(facesContext);
>+ assertTrue(phaseListener.isBeforePhaseCalled());
>+ assertTrue(phaseListener.isAfterPhaseCalled());
>+
>+
>+ }
>+
>+ public void doTestPhaseMethodBindingAndListener(UIViewRoot root) throws Exception {
>+ PhaseListenerBean phaseListener = new PhaseListenerBean();
>+ PhaseListenerBean phaseListenerBean = new PhaseListenerBean();
>+ facesContext.getExternalContext().getRequestMap().put("bean",
>+ phaseListenerBean);
>+ Class [] args = new Class [] { PhaseEvent.class };
>+ MethodBinding
>+ beforeBinding = facesContext.getApplication().createMethodBinding("#{bean.beforePhase}", args),
>+ afterBinding = facesContext.getApplication().createMethodBinding("#{bean.afterPhase}", args);
>+ root.setBeforePhaseListener(beforeBinding);
>+ root.setAfterPhaseListener(afterBinding);
>+ root.addPhaseListener(phaseListener);
>+ root.encodeBegin(facesContext);
>+ root.encodeEnd(facesContext);
>+ assertTrue(phaseListenerBean.isBeforePhaseCalled());
>+ assertTrue(phaseListenerBean.isAfterPhaseCalled());
>+ assertTrue(phaseListener.isBeforePhaseCalled());
>+ assertTrue(phaseListener.isAfterPhaseCalled());
>+
>+ }
>+
>+
>+
>
> // --------------------------------------------------------- Support Methods
>
>@@ -258,6 +393,32 @@
> vr.setRenderKitId("foo");
> vr.setViewId("bar");
> vr.setLocale(new Locale("fr", "FR"));
>+ }
>+
>+ public static class PhaseListenerBean extends Object implements PhaseListener {
>+ private boolean beforePhaseCalled = false;
>+ private boolean afterPhaseCalled = false;
>+
>+ public PhaseListenerBean() {}
>+
>+ public boolean isBeforePhaseCalled() {
>+ return beforePhaseCalled;
>+ }
>+
>+ public boolean isAfterPhaseCalled() {
>+ return afterPhaseCalled;
>+ }
>+
>+ public void beforePhase(PhaseEvent e) {
>+ beforePhaseCalled = true;
>+ }
>+
>+ public void afterPhase(PhaseEvent e) {
>+ afterPhaseCalled = true;
>+ }
>+
>+ public PhaseId getPhaseId() { return PhaseId.RENDER_RESPONSE; }
>+
> }
>
>
>Index: jsf-api/test/javax/faces/mock/MockExternalContext.java
>===================================================================
>RCS file: /cvs/javaserverfaces-sources/jsf-api/test/javax/faces/mock/MockExternalContext.java,v
>retrieving revision 1.13
>diff -u -r1.13 MockExternalContext.java
>--- jsf-api/test/javax/faces/mock/MockExternalContext.java 26 Feb 2004 20:31:51 -0000 1.13
>+++ jsf-api/test/javax/faces/mock/MockExternalContext.java 11 Nov 2004 16:07:25 -0000
>@@ -160,6 +160,9 @@
> if (name.equals(javax.faces.application.StateManager.STATE_SAVING_METHOD_PARAM_NAME)) {
> return null;
> }
>+ if (name.equals(javax.faces.webapp.FacesServlet.LIFECYCLE_ID_ATTR)) {
>+ return null;
>+ }
> throw new UnsupportedOperationException();
> }
>
>
>
>
>
>
>
>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe_at_javaserverfaces.dev.java.net
For additional commands, e-mail: dev-help_at_javaserverfaces.dev.java.net