Kawu
Kawu

Reputation: 14003

JBoss AS 7.1.1.Final: serverError: class java.lang.IllegalStateException on JSF 2 view validation

I am having problems with a pure JSF 2 page. When loading the page two inputText and one select is shown. There's a validation field to the right of each. The two inputs are required fields, while the select always has a selection, so it doesn't validate at all (at least it shouldn't).

Here's a screenshot:

enter image description here

This is what is displayed when the Create button was clicked. When re-hitting the Create button the following popup appears:

enter image description here

OK, nothing without code, here's the XHTML:

<?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:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">
  <h:head>
  </h:head>
  <h:body>
    <h:form>
      <h:panelGrid columns="3" id="base-data-grid">
        <h:outputLabel value="Name:*" for="name-input" />
        <h:inputText value="#{testBean.name}"
                     requiredMessage="Name required!"
                     id="name-input">
          <f:validateRequired />
        </h:inputText>
        <h:message for="name-input" style="color: red;" />
        <h:outputLabel value="Code:*" for="code-input" />
        <h:inputText value="#{testBean.code}"
                     requiredMessage="Code required!"
                     id="code-input">
          <f:validateRequired />
        </h:inputText>
        <h:message for="code-input" style="color: red;" />
        <h:outputLabel value="Location:" for="location-select" />
        <h:selectOneMenu value="#{testBean.location}"
                         converter="#{testConverter}"
                         id="location-select">
          <f:selectItems value="#{testBean.locations}"
                         var="l"
                         itemValue="#{l}"
                         itemLabel="#{l.name}" />
        </h:selectOneMenu>
        <h:message for="location-select" style="color: red;" />
      </h:panelGrid>
      <h:panelGrid columns="1">
        <h:commandButton value="Create"
                           action="#{testBean.create}">
          <f:ajax execute="base-data-grid" render="base-data-grid lalala" />
        </h:commandButton>
      </h:panelGrid>
      <h:messages id="lalala" />
    </h:form>
  </h:body>

</html>

The location class for the select (just a name):

public class Location
{
    private String name;
    public Location(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return name;
    }
}

The converter (as CDI version):

import javax.enterprise.context.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.inject.Inject;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named
@RequestScoped
public class TestConverter implements Converter
{
    private static final Logger log = LoggerFactory.getLogger(TestConverter.class);

    @Inject
    private TestBean testBean;

    @Override
    public Object getAsObject(FacesContext arg0, UIComponent arg1, String name)
    {
        log.info(getClass().getSimpleName() + ".getAsObject: " + name);

        return testBean.getLocationFor(name);
    }

    @Override
    public String getAsString(FacesContext arg0, UIComponent arg1, Object obj)
    {
        log.info(getClass().getSimpleName() + ".getAsString: " + obj);

        return ((Location)obj).getName();
    }
}

And the test bean:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named
@SessionScoped
public class TestBean implements Serializable
{
    private static final Logger log = LoggerFactory.getLogger(TestBean.class);

    private String name;
    private String code;

    private Location location;
    private List<Location> locations;

    @PostConstruct
    public void init()
    {
        locations = new ArrayList<Location>();

        locations.add(new Location("Berlin"));
        locations.add(new Location("London"));
        locations.add(new Location("New York"));
        locations.add(new Location("Moscow"));
        locations.add(new Location("Bejing"));

        location = locations.get(2);
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getCode()
    {
        return code;
    }

    public void setCode(String code)
    {
        this.code = code;
    }

    public Location getLocation()
    {
        return location;
    }

    public void setLocation(Location location)
    {
        this.location = location;
    }

    public List<Location> getLocations()
    {
        return locations;
    }

    public void setLocations(List<Location> locations)
    {
        this.locations = locations;
    }

    public void create()
    {
        log.info("Creating new whatever...");
    }

    // for converter to mimic DB query
    public Location getLocationFor(String name)
    {
        for ( Location location : locations )
        {
            if ( location.getName().equals(name) )
            {
                return location;
            }
        }

        return null;       
    }

}

As you can see, there's not much the page is supposed to do. Validate the two input fields, display the select and call testBean.create if all is validated fine.

However, as soon as the validation has run once, each subsequent Create button click will cause that serverError.

NOTE, that if you remove the f:selectItems or the h:selectOneMenu entirely, the page will work as expected. This is what makes the whole thing so strange actually...

I have no idea what is going on here. Does anybody know what's wrong?

I have attached a JBoss AS 7 test app here: https://community.jboss.org/thread/202501 (dupe post, sorry need a lot of help).

Please have a look at this extremely weird IllegalStateException. There's not even something in the JBAS server.log...

PS: the Mojarra version is 2.1.7 of course (the one that comes with JBoss AS 7.1.1.Final)

Upvotes: 0

Views: 1190

Answers (1)

Nils Otto
Nils Otto

Reputation: 611

Your problem is that the Location needs to be serializable
(implement the java.io.Serializable interface)

the f:validateRequired tag causes the required-validation to be performed by ajax.

During the RestoreView-phase of this ajax-request, the runtime encounters an InstantiationException due to the Location not being Serializable.

I do honestly not know why the error including stacktrace is not logged.

Upvotes: 1

Related Questions