myfaces _DeltaList and PartialStateHolder
-----------------------------------------

                 Key: MYFACES-3228
                 URL: https://issues.apache.org/jira/browse/MYFACES-3228
             Project: MyFaces Core
          Issue Type: Bug
          Components: Archetype
    Affects Versions: 2.1.1
         Environment: tomcat 7.0.14 myfaces 2.1.1 mojarra 2.1.1-b04
            Reporter: lkw
            Priority: Minor


hi.
at first, i'm not native english speaker
and this subject which i will explain is something complex.
so forgive me please even though cant easily understand this.

this is something uncommon case. let's see follow code.



javax.faces.PARTIAL_STATE_SAVING=true
javax.faces.STATE_SAVING_METHOD=client

===
package com.mycompany.mavenproject31;

import java.io.IOException;
import javax.faces.component.ActionSource2;
import javax.faces.component.PartialStateHolder;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagHandler;

public class AnotherActionListenerTagHandler extends TagHandler {
        static public class AnotherActionListener implements ActionListener, 
PartialStateHolder {
                transient boolean isInitialState = false, isTransient = false;
                Integer state1 = null;

                public AnotherActionListener() {
                        state1 = 100;
                }

                @Override
                public void processAction(ActionEvent actionEvent) throws 
AbortProcessingException {
                        return;
                }

                @Override
                public void clearInitialState() {
                        isInitialState = false;
                }
                @Override
                public boolean initialStateMarked() {
                        return isInitialState;
                }
                @Override
                public void markInitialState() {
                        isInitialState = true;
                }

                @Override
                public Object saveState(FacesContext context) {
                        if(isInitialState==true || isTransient==true)
                                return null;
                        return state1;
                }
                @Override
                public void restoreState(FacesContext context, Object state) {
                        state1 = (Integer) state;
                }

                @Override
                public boolean isTransient() {
                        return isTransient;
                }
                @Override
                public void setTransient(boolean newTransientValue) {
                        isTransient = newTransientValue;
                }

                public Integer getState1() {
                        return state1;
                }
                public void setState1(Integer state1) {
                        boolean nullFlag1 = this.state1 == null, nullFlag2 = 
state1 == null;
                        if(nullFlag1==nullFlag2 && nullFlag1==true)
                                return;
                        if(nullFlag1!=nullFlag2)
                                clearInitialState();
                        else
                                if(this.state1.equals(state1)==false)
                                        clearInitialState();
                        this.state1 = state1;
                }

        }
        public AnotherActionListenerTagHandler(TagConfig tagConfig) {
                super(tagConfig);
        }

        @Override
        public void apply(FaceletContext ctx, UIComponent parent) throws 
IOException {
                if(ComponentHandler.isNew(parent)==false)
                        return;
                ActionSource2 actionSource2 = (ActionSource2) parent;
                actionSource2.addActionListener(new AnotherActionListener());
        }
}

<tag>
        <tag-name>anotherActionListener</tag-name>
        
<handler-class>com.mycompany.mavenproject31.AnotherActionListenerTagHandler</handler-class>
</tag>

===

and follow is test code(not unit test!).
===

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";>
<html xmlns="http://www.w3.org/1999/xhtml";
      xmlns:h="http://java.sun.com/jsf/html";
          xmlns:f="http://java.sun.com/jsf/core";
          xmlns:sample2="http://sample.sample2";>
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
                <h:form>
                        <h:commandButton value="iii" 
binding="#{anotherManagedBean.iii}">
                                <sample2:anotherActionListener/>
                                <sample2:anotherActionListener/>
                        </h:commandButton>
                        <h:commandButton value="aaa" 
actionListener="#{anotherManagedBean.aaaActionListener}"/>
                </h:form>
    </h:body>
</html>



package com.mycompany.mavenproject31;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UICommand;
import javax.faces.event.ActionEvent;

@ManagedBean
@ViewScoped
public class AnotherManagedBean implements Serializable {
        transient UICommand iii;

        public void aaaActionListener(ActionEvent event) {
                AnotherActionListenerTagHandler.AnotherActionListener listener 
= (AnotherActionListenerTagHandler.AnotherActionListener) 
iii.getActionListeners()[0];
                listener.setTransient(true);
                listener = 
(AnotherActionListenerTagHandler.AnotherActionListener) 
iii.getActionListeners()[1];
                listener.setState1(101);
                return;
        }

        public UICommand getIii() {
                return iii;
        }
        public void setIii(UICommand iii) {
                this.iii = iii;
        }
}
===

and test as follow.

1.load index2.xhtml
2.push "iii" button and check AnotherActionListener.processAction method's 
twice call.
3.push "aaa" button.
4.push "iii" button again and check listener method's twice call. but method 
call count is just one.

i testet this with myfaces 2.1.1 and mojarra-2.1.1-b04.
in mojarra this is fine,
but in myfaces, something strange.

i found something strange point in source code, but i cant explain detailed.
so i just point source code line number.
that is _DeltaList.java's 231-234 line.
delete these lines and duplicated counter variable(local variable name is "int 
j").
i think that duplicated counter("j") is not necessary.

but in common case,
because we dont use listener or validator as stateful object which is often 
state changed,
instead at use listener or validator as stateful object which is almost not 
state changed.
this is not explicit big problem.
but i think that this is potentially problem.

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to