Nullpointer textChange on textfield with required=true

Dear,

When the event textChange is triggered on a TextField with required = true vaadin throws a Nullpointer.
If i set the textfield setNullSettingAllowed on true the error is gone.

Ewout

Hi Ewout,

Could you add a code example with this behavior?

Hi Felype,

I use the following code:



Example 1:

textSubject = new TextField(VaadinMessageSource.getMessage("agenda.item.detail.subject"));
textSubject.setImmediate(true);
textSubject.setNullRepresentation("");
textSubject.setNullSettingAllowed(true);
textSubject.setSizeFull();
textSubject.setRequired(true);
textSubject.setMaxLength(50);
binder.bind(textSubject, "caption");

Where the binder is a BeanFieldGroup


Solution1

When i leave the line “textSubject.setNullSettingAllowed(true);” the core produces a nullpointer.


Solution2

Set required on false



Example 2


On enter text field:

[code]
public class ImesEnterTextField extends ImesTextField implements FieldEvents.BlurListener, FieldEvents.FocusListener {
private ShortcutListener shortcutListener;
private List listeners = new ArrayList();

public ImesEnterTextField(String caption, ImesEnterPressedListener enterPressedListener) {
    this(enterPressedListener);
    setCaption(VaadinMessageSource.getMessage(caption));
}

public ImesEnterTextField(ImesEnterPressedListener enterPressedListener) {
    super();
    initializeShortcutListener();

    addBlurListener(this);
    addFocusListener(this);

    addEnterPressedListener(enterPressedListener);
}

private void initializeShortcutListener() {
    shortcutListener = new FocusShortcut(this, ShortcutAction.KeyCode.ENTER, null) {
        @Override
        public void handleAction(Object sender, Object target) {
            fireEvent();
        }
    };
}

@Override
public void blur(FieldEvents.BlurEvent event) {
    removeShortcutListener(shortcutListener);
}

@Override
public void focus(FieldEvents.FocusEvent event) {
    addShortcutListener(shortcutListener);
}

public final synchronized void addEnterPressedListener(ImesEnterPressedListener listener) {
    listeners.add(listener);
}

private synchronized void fireEvent() {
    ImesEnterPressedEvent event = new ImesEnterPressedEvent(this);

    Iterator i = listeners.iterator();

    while (i.hasNext()) {
        ((ImesEnterPressedListener) i.next()).enterPressed(event);
    }
}

[/code]Then i make an implementation from this object:
textSearch = new ImesEnterTextField("delivery.confirmation.search.inputPrompt", this); textSearch.setNullSettingAllowed(true); textSearch.setWidth("400px"); textSearch.setRequired(true); When i leave the object on required Vaadin throws a nullpointer.

Solution

If required is false then everything works.

Vaadin version: 7.1.13

Kind regards,

Ewout

Dear Felype,

Did you find the time to investigate this problem?

Kind regards,

Ewout

Hello Ewout,

I tried to reproduce the issue with the code provided, but unfortunately failed. Could you also provide the stacktrace for the nullpointer?

Best regards,
Matti

Matti,

I do not have a stacktrace, it’s a vaadin internal error.

This is a full class with the same problem

[code]
public class NewFlowDetailWindow extends ImesWindow implements Button.ClickListener, Property.ValueChangeListener {
private TasksView tasksView;
private VerticalLayout mainVerticalLayout;
private HorizontalLayout horizontalLayout1;
private ImesComboBox flowPriorityCombobox;
private ImesComboBox flowComboxBox;
private ImesPopupDateTimeField flowDueDate;
private ImesTextArea descriptionField;
private CustomerSearchComboBox comboCustomerSearch;
private ImesComboBox assigneeCombobox;
private ImesButton createButton;
private ImesTextField taskNameField;
private ImesBeanItemContainer flowDTOImesBeanItemContainer;
private ImesBeanItemContainer priorityDTOImesBeanItemContainer;
private ImesBeanItemContainer userDTOImesBeanItemContainer;
private String currentFlowIdentifier;

public NewFlowDetailWindow(TasksView view) {
    super(WindowSize.SMALL);
    tasksView = view;

    mainVerticalLayout = new VerticalLayout();
    mainVerticalLayout.setSpacing(true);
    mainVerticalLayout.setMargin(true);

    horizontalLayout1 = new HorizontalLayout();
    horizontalLayout1.setWidth("100%");
    horizontalLayout1.setSpacing(true);

    flowComboxBox = new ImesComboBox("tasks.detail.flow");
    flowComboxBox.setRequired(true);
    flowComboxBox.setImmediate(true);
    flowComboxBox.setNullSelectionAllowed(false);
    horizontalLayout1.addComponent(flowComboxBox);
    flowDTOImesBeanItemContainer = new ImesBeanItemContainer<FlowDTO>(FlowDTO.class);
    flowDTOImesBeanItemContainer.addAll(view.getAllStartableFromTaskSectionFlows());
    flowComboxBox.setContainerDataSource(flowDTOImesBeanItemContainer);
    flowComboxBox.setItemCaptionPropertyId("flowDisplayName");
    flowComboxBox.addValueChangeListener(this);

    flowPriorityCombobox = new ImesComboBox("tasks.detail.prio");
    horizontalLayout1.addComponent(flowPriorityCombobox);
    List<PriorityDTO> l = ActivitiUtil.getPossiblePriorities();
    PriorityDTO standardSelected = null;
    for (PriorityDTO dto : l) {
        dto.setDisplayName(VaadinMessageSource.getMessage(dto.getResourceBundle()));
        if (dto.getPriorityType().equals(EnumDefinitions.TaskPriorityType.MEDIUM)) {
            standardSelected = dto;
        }
    }
    priorityDTOImesBeanItemContainer = new ImesBeanItemContainer<PriorityDTO>(PriorityDTO.class);
    priorityDTOImesBeanItemContainer.addAll(l);
    flowPriorityCombobox.setContainerDataSource(priorityDTOImesBeanItemContainer);
    flowPriorityCombobox.setItemCaptionPropertyId("displayName");
    flowPriorityCombobox.setImmediate(true);
    flowPriorityCombobox.setNullSelectionAllowed(false);
    flowPriorityCombobox.setValue(standardSelected);

    flowDueDate = new ImesPopupDateTimeField("tasks.detail.due.date", ActivitiUtil.getDefaultFutureDateActiviti());
    horizontalLayout1.addComponent(flowDueDate);

    mainVerticalLayout.addComponent(horizontalLayout1);

    descriptionField = new ImesTextArea("tasks.detail.description");
    descriptionField.setWidth("100%");
    descriptionField.setMaxLength(200);
    mainVerticalLayout.addComponent(descriptionField);

    comboCustomerSearch = new CustomerSearchComboBox("tasks.detail.customer", tasksView);
    comboCustomerSearch.setWidth("60%");
    mainVerticalLayout.addComponent(comboCustomerSearch);

    assigneeCombobox = new ImesComboBox("tasks.detail.assignee");
    assigneeCombobox.setWidth("60%");
    assigneeCombobox.setFilteringMode(FilteringMode.CONTAINS);
    userDTOImesBeanItemContainer = new ImesBeanItemContainer<UserDTO>(UserDTO.class);
    userDTOImesBeanItemContainer.addAll(view.getAllUsers());
    assigneeCombobox.setContainerDataSource(userDTOImesBeanItemContainer);
    assigneeCombobox.setItemCaptionPropertyId("name");
    mainVerticalLayout.addComponent(assigneeCombobox);

    createButton = new ImesButton("tasks.detail.create.flow");
    createButton.addClickListener(this);
    addButtonToButtonBar(createButton);

    setContent(mainVerticalLayout);
}

@Override
public void buttonClick(Button.ClickEvent event) {
    boolean success = false;
    if (validate() && flowComboxBox.getValue() != null && (StringUtils.equals(BackendConstants.ACTIVITI_SIMPLE_TASK_FLOW_ID, ((FlowDTO) flowComboxBox.getValue()).getFlowId()))) {
        //initiate a one task flow
        success = tasksView.startOneTaskFlow(((UserDTO) assigneeCombobox.getValue()).getUserName(), collectFlowGeneralData(), taskNameField.getValue(), descriptionField.getValue());
    }

    if (success) {
        CrmSfaApplicationUI.getCurrent().removeWindow(this);
    }
}

private FlowGeneralData collectFlowGeneralData() {
    FlowGeneralData data = new FlowGeneralData();
    data.setEmailInitiator(((CrmSfaApplicationUI) UI.getCurrent()).getUserData().getEmail());
    data.setInitiator(((CrmSfaApplicationUI) UI.getCurrent()).getUserData().getLoginName());
    if (flowPriorityCombobox.getValue() != null) {
        data.setPriority(((PriorityDTO) flowPriorityCombobox.getValue()).getPriorityType());
    } else {
        data.setPriority(EnumDefinitions.TaskPriorityType.MEDIUM);
    }
    data.setFlowIdentifier(((FlowDTO) flowComboxBox.getValue()).getFlowId());
    if (comboCustomerSearch.getValue() != null) {
        data.setPlantId(((CustomerSearchDTO) comboCustomerSearch.getValue()).getId());
    }
    data.setFlowDueDate(flowDueDate.getValue());
    return data;
}

private void clearPreviousErrors() {
    flowComboxBox.removeFieldError();
    flowDueDate.removeFieldError();
    flowPriorityCombobox.removeFieldError();
    assigneeCombobox.removeFieldError();
    descriptionField.removeFieldError();
}

@Override
public void valueChange(Property.ValueChangeEvent event) {
    //the flow type is changed and depending on the chosen flow, we will have different logic
    if (flowComboxBox.getValue() != null
            && (StringUtils.equals(BackendConstants.ACTIVITI_SIMPLE_TASK_FLOW_ID, ((FlowDTO) flowComboxBox.getValue()).getFlowId()))) {
        //a simple 1 step flow is chosen
        currentFlowIdentifier = BackendConstants.ACTIVITI_SIMPLE_TASK_FLOW_ID;
        assigneeCombobox.setRequired(true);
        flowPriorityCombobox.setRequired(true);
        flowDueDate.setRequired(true);
        comboCustomerSearch.setRequired(false);
        taskNameField = new ImesTextField("tasks.detail.name");
        taskNameField.setRequired(true);
        taskNameField.setMaxLength(64);
        taskNameField.setWidth("100%");
        mainVerticalLayout.addComponent(taskNameField, 1);
    } else {
        //restore all as if no flow is chosen
        assigneeCombobox.setRequired(false);
        flowPriorityCombobox.setRequired(false);
        flowDueDate.setRequired(false);
        comboCustomerSearch.setRequired(false);
        currentFlowIdentifier = null;
        mainVerticalLayout.removeComponent(taskNameField);
    }
}

private boolean validate() {
    boolean valid = true;
    clearPreviousErrors();
    if (flowComboxBox.getValue() != null
            && (StringUtils.equals(BackendConstants.ACTIVITI_SIMPLE_TASK_FLOW_ID, ((FlowDTO) flowComboxBox.getValue()).getFlowId()))) {
        if (flowComboxBox.getValue() == null) {
            valid = false;
            flowComboxBox.setComponentError(new ImesErrorMessage("tasks.detail.create.flowtype.error"));
        }
        if (flowDueDate.getValue() == null) {
            valid = false;
            flowDueDate.setComponentError(new ImesErrorMessage("tasks.detail.create.due.date.error"));
        } else {
            DateTime dt = new DateTime(flowDueDate.getValue());
            if (dt.isBeforeNow()) {
                flowDueDate.setComponentError(new ImesErrorMessage("tasks.detail.create.due.date.error"));
            }
        }
        if (flowPriorityCombobox.getValue() == null) {
            valid = false;
            flowPriorityCombobox.setComponentError(new ImesErrorMessage("tasks.detail.create.priority.error"));
        }
        if (assigneeCombobox.getValue() == null) {
            valid = false;
            assigneeCombobox.setComponentError(new ImesErrorMessage("tasks.detail.create.assignee.error"));
        }
        if (StringUtils.isEmpty(taskNameField.getValue())) {
            valid = false;
            taskNameField.setComponentError(new ImesErrorMessage("tasks.detail.create.task.name.required"));
        }

    } else {
        //no flow selected yet
        if (flowComboxBox.getValue() == null) {
            valid = false;
            flowComboxBox.setComponentError(new ImesErrorMessage("tasks.detail.create.flowtype.error"));
        }
        if (StringUtils.isEmpty(descriptionField.getValue())) {
            valid = false;
            descriptionField.setComponentError(new ImesErrorMessage("tasks.detail.create.priority.description"));
        }
    }
    return valid;
}

}
[/code]when we open the window

[code]
public class ImesWindow extends Window {
public enum WindowSize {
TINY, SMALL, SMALLWITHSIDEBAR, MEDIUM, WIDE, LARGE
}

private Panel contentPanel = null;
private VerticalLayout mainLayout = null;
private HorizontalLayout buttonLayout = null;

public void setTranslationCaption(String caption) {
    super.setCaption(VaadinMessageSource.getMessage(caption));
}

public ImesWindow(WindowSize windowSize) {
    setModal(true);
    setResizable(false);

    mainLayout = new VerticalLayout();

    contentPanel = new Panel();
    contentPanel.setSizeFull();

    buttonLayout = new HorizontalLayout();
    buttonLayout.setWidth("100%");
    buttonLayout.setSpacing(true);
    buttonLayout.addStyleName("buttonbar");
    buttonLayout.setVisible(false);

    mainLayout.addComponent(contentPanel);
    mainLayout.addComponent(buttonLayout);
    mainLayout.setExpandRatio(contentPanel, 1.0f);

    super.setContent(mainLayout);

    resize(windowSize);

    addShortcutListener(new CloseShortcut(this, ShortcutAction.KeyCode.ESCAPE));
    CrmSfaApplicationUI.getCurrent().addWindow(this);
}

public final void resize(WindowSize windowSize) {
    switch (windowSize) {
        case TINY:
            setWidth("400px");
            break;
        case SMALL:
            setWidth("600px");
            break;
        case MEDIUM:
            setWidth("800px");
            setHeight("90%");

            mainLayout.setSizeFull();
            break;
        case WIDE:
            setWidth("1100px");
            setHeight("90%");

            mainLayout.setSizeFull();
            break;
        case LARGE:
            setWidth("90%");
            setHeight("90%");

            mainLayout.setSizeFull();
    }
    center();
}

@Override
public void setContent(Component newContent) {
    if (contentPanel == null) {
        super.setContent(newContent);
    } else {
        contentPanel.setContent(newContent);
    }
}

public void addButtonToButtonBar(Component button) {
    buttonLayout.addComponent(button);
    buttonLayout.setVisible(true);
}

public void addButtonToButtonBar(Component button, Alignment alignment) {
    buttonLayout.addComponent(button);
    buttonLayout.setComponentAlignment(button, alignment);
    buttonLayout.setVisible(true);
}

public void addButtonToButtonBar(Component button, Alignment alignment, float expantionRatio) {
    buttonLayout.addComponent(button);
    buttonLayout.setComponentAlignment(button, alignment);
    buttonLayout.setExpandRatio(button, expantionRatio);
    buttonLayout.setVisible(true);
}

public void addButtonToButtonBar(Component button, float expantionRatio) {
    buttonLayout.addComponent(button);
    buttonLayout.setExpandRatio(button, expantionRatio);
    buttonLayout.setVisible(true);
}

public void removeButtonFromButtonBar(Component button) {
    if (button != null) {
        buttonLayout.removeComponent(button);
    }
}

}
[/code]When we debug into the core of vaadin it goes wrong on the point that the UidlRequestHandler cals write on the UidlWirter class.

This method loops over the clientconnectors and cales beforeClientRespons on the connector.

When we dive into this method we end up in the AbstractField where the getErrorMessage throws the nullpointer.

All,

I found the problem. I did an overwrite of the getValue on a TextField like this:

@Override
    public String getValue() {
        return StringUtils.trimToNull(super.getValue());
    }

Thanx for the reply and sorry for your time.

Best regards and keep up the good work,

Ewout Seldeslachts