0

I'm just trying out JSF an ran into an ugly ClassCastException.

I have a Managed Bean (CustomerBean) that has a POJO (Customer) where I store the user data. One of the POJO's properties is List<CathegoryType.Type> preferredCathegories (with getter and setter). CathegoryType is a model class which provides cathegories (through a nested enum Type) and their localized names (through a method getCathegory(Type type)).

Now, I have one JSF page to enter some user data (editCustomer.xhtml). There is a section to select preferred cathegories. The JSF code to select the cathegories looks like this:

<h:selectManyListbox 
        id="pcat"
        value="#{customerBean.customer.preferredCathegories}"
        styleClass="form-control">
    <f:selectItems 
            value="#{customerBean.cathegoryTypes}">
    </f:selectItems>
</h:selectManyListbox>

The renderes component looks like this: The renderes <code>selectMAnyListBox</code>

The field List<SelectItem> CustomerBeand.cathegoryTypes provides the mapping of the enum literals to their names like new SelectItem(type, CathegoryType.getCathegory(type)). Now there comes the tricky part that throws the ClassCastException (in my understanding for no reason!)

When I submit the form an other JSF page (showCustomer.xhtml) is should present the just entered user data. But creating the view terminates with throwing the following ClassCastException:

SCHWERWIEGEND: Servlet.service() for servlet [Faces Servlet] in context with path [/...] threw exception [java.lang.String cannot be cast to ...CathegoryType$Type] with root cause
java.lang.ClassCastException: java.lang.String cannot be cast to ...CathegoryType$Type
    at ...CustomerBean.getNamedPreferredCathegories(CustomerBean.java:247)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    ...

To present the chosen cathegories, showCustomer.xhtml calls a method String CustomerBean.getNamedPreferredCathegories():

<h:outputText value="#{customerBean.namedPreferredCathegories}" />

This method calculates a String of the chosen cathegories:

def String getNamedPreferredCathegories() {
    val StringBuilder names = new StringBuilder
    val List<CathegoryType.Type> cathegories = customer.preferredCathegories
    val ListIterator<CathegoryType.Type> iter = cathegories.listIterator
    var boolean first = true
    while (iter.hasNext) {
        val CathegoryType.Type cat = iter.next
        val String name = cathegoryType.getCathegory(cat)
        if(first) first = false else names.append(', ')
        names.append(name)
    }
    return names.toString
}

I'm using the Xtend programming languate. It is a JVM language (compiles to Java code) and thus fully compatible with the Java typesystem. I tried to write this method as Java like as possible, normally it can be done in one single line:

def String getNamedPreferredCathegories() {
    '''«FOR name : customer.preferredCathegories.map[cathegory] SEPARATOR ', '»«name»«ENDFOR»'''.toString
}

Since I do any iteration over the list, the ClassCastException is thrown ... but as one can see, I do not perform any cast operation in my code! So where does the exception comes from?

I run the project on Tomcat 8.5.9.

Edit: Ok, the generated Java method of my Xtend method is theo following:

public String getNamedPreferredCathegories() {
    final StringBuilder names = new StringBuilder();
    final List<CathegoryType.Type> cathegories = this.customer.getPreferredCathegories();
    final ListIterator<CathegoryType.Type> iter = cathegories.listIterator();
    boolean first = true;
    while (iter.hasNext()) {
        {
            final CathegoryType.Type cat = iter.next(); // Exception is thrown here!
            final String name = this.cathegoryType.getCathegory(cat);
            if (first) {
                first = false;
            } else {
                names.append(", ");
            }
            names.append(name);
        }
    }
    return names.toString();
}

The exception is thrown in the marked line (final CathegoryType.Type cat = iter.next();). Additionally the generated Java parts of the related Customer class are this:

public class Customer {

    private List<CathegoryType.Type> preferredCathegories;

    public List<CathegoryType.Type> getPreferredCathegories() {
        return this.preferredCathegories;
    }

    public void setPreferredCathegories(final List<CathegoryType.Type> preferredCathegories) {
        this.preferredCathegories = preferredCathegories;
    }
}

Additionally, I will have a look at the linked question that might discuss the exact same problem.

Joko
  • 600
  • 3
  • 15
  • You might want to share the generated .java for that .xtend, at least the part that creates line 247. My first guess is that customer.preferredCategories actually returns a "poluted" List that contains (at least some) Strings instead of CathegoryType.Type. – k5_ Jan 03 '17 at 21:10
  • Ok, k5_, I added the requested Java code snippets for you, but, as you can see, my question is a duplicate. I looked the linked question an got an answer. Thanks BalusC for your advice! – Joko Jan 04 '17 at 15:32

0 Answers0