Ошибка проверки JSF: недопустимое значение

Я знаю, что это кажется распространенным, но я потерялся с этим. Происходит при нажатии кнопки «Добавить» в Assessment.jsf. Во всяком случае, я приложил то, что я думаю, соответствующие разделы.

FWIW, AssessmentType.equals() не запускается при отладке.

Заранее спасибо.

j_idt38:j_idt47:j_idt48: Validation Error: Value is not valid

оценка.xhtml:

        <h:form>
            <h:selectOneMenu value="#{assessmentBean.assessmentField}">
                <f:selectItems value="#{assessmentBean.assessment.type.fields}" />
            </h:selectOneMenu>

            <h:commandButton value="Add" action="#{assessmentBean.doAddField}">
                <f:param name="assessmentId"
                    value="#{assessmentBean.assessment.id}" />
            </h:commandButton>
        </h:form>

оценка.jsf:

<form id="j_idt38:j_idt47" name="j_idt38:j_idt47" method="post" action="/jsf-web/edit/assessment.jsf" enctype="application/x-www-form-urlencoded"> 
<input type="hidden" name="j_idt38:j_idt47" value="j_idt38:j_idt47" /> 
<select name="j_idt38:j_idt47:j_idt48" size="1">    <option value="1">Presenting Condition</option> 
    <option value="2">Problem Duration</option> 
</select> 
<script type="text/javascript" src="/jsf-web/javax.faces.resource/jsf.js.jsf?ln=javax.faces"></script>
<input type="submit" name="j_idt38:j_idt47:j_idt50" value="Add" onclick="mojarra.jsfcljs(document.getElementById('j_idt38:j_idt47'),{'j_idt38:j_idt47:j_idt50':'j_idt38:j_idt47:j_idt50','assessmentId':'1'},'');return false" /><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="3431661972220941645:6952134510589038883" autocomplete="off" /> 
</form> 

Тип оценки.java:

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotNull;

import lombok.Data;

import org.hibernate.envers.Audited;

@Audited
@Data
@Entity
public class AssessmentType implements Comparable<AssessmentType> {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private String name;

    @OneToMany( fetch=FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=AssessmentField.class )
    private List<AssessmentField> fields;

    @Override
    public int compareTo(final AssessmentType o) {
        return getId().compareTo(o.getId());
    }

    @Override
    public String toString() {
        return getName();
    }
}

AssessmentFieldConverter.java

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.htu.fizio.api.AssessmentFieldManager;
import com.htu.fizio.domain.AssessmentField;

@FacesConverter(forClass = AssessmentField.class)
public class AssessmentFieldConverter implements Converter {

    AssessmentFieldManager<AssessmentField> assessmentFieldManager;

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public Object getAsObject(FacesContext ctx, UIComponent component, String value) {
        try {
            final InitialContext ic = new InitialContext();

            assessmentFieldManager = (AssessmentFieldManager) ic.lookup("fizio/AssessmentFieldManagerImpl/local");

            return assessmentFieldManager.find(Long.valueOf(value));
        } catch (NamingException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public String getAsString(FacesContext ctx, UIComponent component, Object value) {
        return String.valueOf(((AssessmentField) value).getId());
    }
}

AssessmentBean.java

    import java.util.List;

    import javax.annotation.PostConstruct;
    import javax.ejb.EJB;
    import javax.faces.bean.ManagedBean;
    import javax.faces.bean.ManagedProperty;
    import javax.faces.bean.SessionScoped;

    import lombok.Getter;
    import lombok.Setter;

    import com.htu.fizio.api.AssessmentManager;
    import com.htu.fizio.domain.Assessment;
    import com.htu.fizio.domain.AssessmentField;
    import com.htu.fizio.domain.AssessmentFieldValue;
    import com.htu.fizio.jsf.faces.FacesUtil;

...    

@PostConstruct
public void init() {
    if (FacesUtil.containsKey("assessmentId")) {
        final Long id = Long.parseLong(FacesUtil.get("assessmentId"));

        assessment = assessmentManager.find(id);
    } else {
        assessment = new Assessment();
    }
}

    public String doAddField() {
        final AssessmentFieldValue value = new AssessmentFieldValue();

        value.setField(assessmentField);
        value.setValue("");

        assessment.getFieldValues().add(value);

        assessmentManager.save(assessment);

        return "/edit/assessment";
    }

Редактировать:

Только что заметил это при отладке, это вероятный подозреваемый?:

Daemon Thread [HandshakeCompletedNotify-Thread] (Suspended (exception ConcurrentModificationException)) 
    HashMap$EntryIterator(HashMap$HashIterator<E>).nextEntry() line: 793    
    HashMap$EntryIterator.next() line: 834  
    HashMap$EntryIterator.next() line: 832  
    SSLSocketImpl$NotifyHandshakeThread.run() line: 2214    

person rich    schedule 12.06.2011    source источник
comment
ConcurrentModificationException выглядит плохо, но, поскольку оно находится в потоке Deamon, очень маловероятно, что оно будет мешать JSF на уровне, изображенном в исходной части вопроса.   -  person Arjan Tijms    schedule 13.06.2011


Ответы (3)


Ошибка проверки: недопустимое значение

По сути, эта ошибка означает, что выбранный элемент не соответствует ни одному из элементов, доступных в списке. т.е. объект, представленный выбранным значением элемента, никогда не возвращал true при вызове equals() ни с одним из доступных элементов выбора.

У этой проблемы всего две причины:

  1. Метод equals() рассматриваемого типа объекта не работает.
  2. Содержимое списка элементов на этапе проверки запроса на отправку формы отличается от того, что было на этапе ответа на рендеринг исходного запроса на отображение формы.

Поскольку первое, кажется, правильно реализовано - согласно комментариям -, единственная оставшаяся причина - вторая. Предполагая, что вы нигде не выполняете бизнес-логику в методе получения, простой тест — поместить #{assessmentBean} в область сеанса. Если это работает, то логика (предварительной) загрузки данных списка выбранных элементов определенно неверна.

person BalusC    schedule 13.06.2011
comment
Спасибо, но я не смог дойти до проблемной страницы с областью сеанса bean-компонента - во-первых, он жаловался, что другие bean-компоненты также должны быть ограничены сеансом, тогда я получил это на более ранней странице: org.hibernate.TransientObjectException: объект ссылается на несохраненный переходный экземпляр — сохраните временный экземпляр перед сбросом: com.htu.fizio.domain.Appointment - person rich; 13.06.2011
comment
@BalusC, какие возможные проблемы могут случиться с моим equals(), чтобы он не работал? - person ; 11.06.2012
comment
@Daniel: если он содержит логическую ошибку кода. - person BalusC; 11.06.2012
comment
@BalusC хорошо ... так что это не моя проблема, я думаю. У меня вопрос по JSF, если можно, посмотрите пожалуйста, он про конвертеры - person ; 11.06.2012

Проверка не проходит, потому что после того, как ваш преобразователь преобразует строковое представление AssessmentType обратно в объект, JSF выполняет итерацию по существующим значениям (assessmentBean.assessment.type.fields) и сравнивает этот недавно преобразованный объект со всеми существующими.

Поскольку вы не реализовали Object#equals для AssessmentType, по умолчанию будет использоваться сравнение идентификаторов объектов (грубо говоря, адрес памяти вашего объекта), что, конечно, не удастся.

Таким образом, решение состоит в том, чтобы либо реализовать Object#equals, либо позволить конвертеру получить объект из assessmentBean.assessment.type.fields, а не из AssessmentTypeManager.

person Arjan Tijms    schedule 12.06.2011
comment
Я использую проект lombok (@Data), который генерирует для меня метод equals, но даже если я реализую свой собственный явно, точка останова метода на нем не срабатывает. То же самое относится и к hashCode(). Я не уверен, почему объект из AssessmentFieldManager должен иметь тип, отличный от объекта в AssessmentBean.assessment.type.fields, который является List‹AssessmentField›? Извиняюсь - только что понял, что вставил конвертер AssessmentType вместо AssessmentField. - person rich; 13.06.2011
comment
Это не будет другой тип, но с другим идентификатором объекта. Для случайного класса без явного метода равенства new Foo().equals(new Foo()) всегда ложно. Преобразователь вернет новый экземпляр, который сравнивается с каждым экземпляром, присутствующим в List<AssessmentField>, возвращаемом assessmentBean.assessment.type.fields. - person Arjan Tijms; 13.06.2011
comment
›но даже если я реализую свой собственный явно, точка останова метода на нем не срабатывает - это меня удивляет. Ваш случай очень типичен, и я видел это десятки раз в своей команде. Что вы могли бы сделать, так это установить точку останова исключения на javax.faces.validator.ValidatorException, чтобы узнать, какой код в JSF вызывает исключение проверки (скорее всего, оно есть). - person Arjan Tijms; 13.06.2011
comment
Я добавил точки останова в четыре конструктора и два метода, которые я вижу в javax.faces.validator.ValidatorException, ни один из которых не запускается. - person rich; 13.06.2011
comment
Мой плохой, извините. Это конкретное сообщение обычно устанавливается не через исключение ValidatorException, а в javax.faces.component.UISelectMany#validateValue. Весьма вероятно, что вы видите сообщение javax.faces.component.UISelectMany.INVALID из пакета ресурсов std JSF (имеет именно такой текст). Насколько мне известно, этот ключ пакета используется только в UISelectMany. - person Arjan Tijms; 13.06.2011
comment
Моя точка останова в этом методе тоже не срабатывает... - person rich; 13.06.2011
comment
Хорошо, но я думаю, что мы сосредотачиваемся на проблеме;) Вы также можете попробовать javax.faces.component.UISelectOne#validateValue, так как текст также может быть javax.faces.component.UISelectOne.INVALID (на самом деле это более вероятно, поскольку вы используете h: selectOneMenu на своем Facelet). - person Arjan Tijms; 13.06.2011
comment
›AssessmentType.equals() не запускается при отладке — это также может означать, что список полностью пуст после обратной передачи. В противном случае он действительно должен вызвать метод equals. - person Arjan Tijms; 14.06.2011
comment
javax.faces.component.UISelectOne#validateValue действительно запускает точку останова! Он предоставляет выбранное AssessmentField в качестве аргумента. Я не вижу, использует ли он UISelectOne.INVALID - я не могу найти банку с исходным кодом для javaee-6? - person rich; 14.06.2011
comment
Для JBoss AS 6 вам не нужен исходный код для javaee-6, но для [jboss home]/server/default/deployers/jsf.deployer/Mojarra-2.0/jsf-libs/jsf-impl-2.0.3-b05 .банка. К сожалению, JBoss немного небрежно предоставляет полные исходные архивы, поэтому вам лучше скачать Mojarra 2.0.3 отдельно (java.net/downloads/javaserverfaces/release/2.0.3/). Если вы используете Eclipse с инструментами JBoss, упомянутый выше jar помещается в путь к вашему классу, и присоединение к исходному коду выполняется относительно легко. - person Arjan Tijms; 14.06.2011
comment
Отлично, похоже, что сообщение UISelectOne.INVALID. Найденной переменной присваивается значение false. Похоже, что getConverter() возвращает null, а в SelectUtils.matchValue items.hasNext() сразу становится false. - person rich; 14.06.2011
comment
Если hasNext() немедленно возвращает значение null, весьма вероятно, что assessmentBean.assessment.type.fields действительно возвращает значение null. В какой-то момент JSF найдет ваш компонент f:selectItems и оценит его значение. Самый быстрый способ найти это — установить точку останова в assesmentBean.getAssesment(), затем в getType() и затем в getFields. - person Arjan Tijms; 14.06.2011
comment
Кроме того, вы также можете установить точку останова в SelectItemsIterator#initializeItems. Вы должны увидеть, почему элементам не присваивается значение. - person Arjan Tijms; 14.06.2011

Я думаю, что решил это, по крайней мере, я перешел к следующей ошибке!

Несмотря на то, что я публиковал кучу кода, я не публиковал полный xhtml, в котором было несколько вложенных тегов формы. Кажется, что только одна форма позволяет передать параметр AssessmentId, который, в свою очередь, позволяет AssessmentBean правильно заполнить список AssessmentFields для типа оценки.

Спасибо за помощь.

person rich    schedule 14.06.2011