Problem with conversation scope using Prettyfaces and Apache Myfaces CODI

Splash Forums PrettyFaces Users Problem with conversation scope using Prettyfaces and Apache Myfaces CODI

This topic contains 10 replies, has 4 voices, and was last updated by  sunnymoon 4 years, 1 month ago.

Viewing 11 posts - 1 through 11 (of 11 total)
  • Author
    Posts
  • #18077

    Victor Lindberg
    Participant

    I am using apache myfaces codi (CDI Extension). Codi has a ViewAccessScoped scope (like Conversation scoped). In pages transitions, the CODI uses a ‘windowId’ url parameter to map and propagate the conversation. Using ‘pretty URLs’ does not work. How can i to map this parameter? How can i solve this problem?

    Thanks.

    #21658

    Hi Victor,

    I believe that you need to add the windowId parameter to any URLs that you wish to generate.

    Could you provide us some more specific information on your configuration and the code you are using to generate your links?

    Thanks,

    Lincoln

    #21661

    @Victor:

    Did you get it working? I would be very interested to know if there are any issues when using CODI with PrettyFaces.

    Thanks

    Christian

    #24568

    sunnymoon
    Participant

    Meanwhile I’ve been investigating this problem a little more… What I’ve found so far:

    1. If there is no path bind expression, then the ViewAccessScoped beans are maintained correctly
    2. If there is a path bind expression to a different bean than the ViewAccessScoped one we are interested in (or looking at), then there is no issue and the ViewAccessScoped beans are actually maintained correctly.
    3. This seems to happen both with Rewrite (with PrettyFaces integration modules) as well as PrettyFaces
    directly.
    4. The ViewAccessScoped bean get’s cleaned up on “injectParams” from rewrite (just before the injection occurs, the old instance is cleared and a new instance is created to serve the request).
    5. The problem is related to the method “isExpired” in class ViewAccessConversationExpirationEvaluator from CODI.

    In this code from the referred method at point 5 above :

    String oldViewId = getOldViewId(); //oldViewId=page1.xhtml

    if (oldViewId != null && oldViewId.equals(this.lastViewId))
    {
    this.lastViewId = getNewViewId(); //strangely, lastViewId becomes=page1.xhtml (which allready was)
    }

    String currentViewId = getCurrentViewId(); // page2.xhtml

    if(currentViewId == null) //in case of an invalid view
    {
    return false;
    }
    boolean result = !currentViewId.equals(this.lastViewId); //so these are not equal and "result" becomes true, saying the context is "expired"
    return result;

    What I suspect is that the getNewViewId() method is somehow corrupt because Codi expects to have it’s own NavigationHandler doing something that Pretty rewrite is not doing the same way.

    May you help further?

    #24569

    sunnymoon
    Participant

    Ok, now It seems I’ve found the root of the problem.

    The order of PrettyFaces Phase Listener and CODI JSFPhaseListener is the root of all evil… It seems as though PrettyFaces phase listener is trying to inject the parameters before the JSFPhaseListener from CODI sets the new View id, and as such the previous code (mentioned in the previous post) fails…

    Now I have to find a way to order the PhaseListeners correctly (CODI first, then PrettyFaces). Any ideas?

    #24573

    You could try to manually put both listeners into the faces-config.xml of your application in the correct order. I think this should work.

    #24582

    sunnymoon
    Participant

    Unfortunately I allready tried the suggestion you made but it didn’t work.

    I tried the following:

    Within my app faces-config.xml set metadata-complete=true.
    Specify both listeners in the correct order and all other information from both prettyfaces and CODI -> doesn’t work.
    Specify absolute-ordering between CODI and prettyfaces -> doesn’t work.

    I managed to narrow it down a little more and it seems CODI is also using a different JSF Lifecycle implementation and Context Factory: CODILifecycleWrapper and CODILifecycleFactoryWrapper.

    This LifecycleFactory from CODI, when instantiating the CODILifecycleWrapper has this code:

    public Lifecycle getLifecycle(String s)
        {
            Lifecycle result = this.wrapped.getLifecycle(s);
    
            if(this.deactivated)
            {
                return result;
            }
            return new CodiLifecycleWrapper(result, PhaseListenerExtension.consumePhaseListeners());
        }

    and the PhaseListenerExtension.consumePhaseListeners does not seem to respect the faces-config phase-listeners order as it uses their own JsfPhaseListener annotation scanner (alongside an InvocationOrder annotation).

    This means I somehow have to surpass the CODILifecycleWrapper and reorder the PhaseListeners as required by my app.

    This seams to be more of a problem with CODI then PrettyFaces, as it doesn’t seem that PrettyFaces is doing anything wrong here…

    • This reply was modified 4 years, 1 month ago by  sunnymoon.
    #24584

    sunnymoon
    Participant

    BTW, a side note:

    The PrettyPhaseListener is only executing on RESTORE_VIEW phase… So why not return RESTORE_VIEW from the getPhaseId() method of the PhaseListener? Just an optimization, I think…

    #24597

    Hmm, good to know that *we* aren’t the cause of the problem, but still concerning that there’s a problem. Perhaps you could submit a bug report to them and reference this forum thread?

    Regarding the PrettyPhaseListener – it will actually execute on any phase where an <action phaseId="RENDER_RESPONSE..." /> etc, has been specified. By default it uses RESTORE_VIEW, but could be any.

    #24603

    sunnymoon
    Participant

    Meanwhile I think I’ve been able to workaround the problem with the following code

    import javax.faces.event.PhaseEvent;
    import javax.faces.event.PhaseId;
    import javax.faces.event.PhaseListener;
    
    import org.apache.myfaces.extensions.cdi.core.api.Advanced;
    import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.WindowContext;
    import org.apache.myfaces.extensions.cdi.jsf.api.listener.phase.JsfPhaseListener;
    import org.apache.myfaces.extensions.cdi.jsf.impl.util.ConversationUtils;
    
    import com.ocpsoft.pretty.PrettyContext;
    
    @JsfPhaseListener
    @Advanced
    public class PrettyFacesToCODIViewAccessScopedAwarePhaseListener implements PhaseListener {
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 121463399007913590L;
    
    	@Override
    	public void afterPhase(PhaseEvent event) {
    		
    	}
    
    	@Override
    	public void beforePhase(PhaseEvent event) {
    		if(PrettyContext.getCurrentInstance()!=null && PrettyContext.getCurrentInstance().getCurrentViewId()!=null) {
    			WindowContext windowContext = ConversationUtils.getWindowContextManager().getCurrentWindowContext();
    			ConversationUtils.storeViewIdAsNewViewId(windowContext, PrettyContext.getCurrentInstance().getCurrentViewId());
    		}
    	}
    
    	@Override
    	public PhaseId getPhaseId() {
    		return PhaseId.RESTORE_VIEW;
    	}
    	}

    Regarding the other optimization I suggested I’m pretty sure that the PrettyPhaseListener class I’ve been debugging has an if checking for the Restore View phase only…

    #24604

    sunnymoon
    Participant

    Regarding my last suggestion, I’ve checked again and the beforePhase method actually does have implementation for other phases…

     else if (!facesContext.getResponseComplete())
          {
             FacesContext context = facesContext;
             messagesUtils.restoreMessages(context, context.getExternalContext().getRequestMap());
             processEvent(event);
          }
Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.

Comments are closed.