Executing the ActionListener of a (Primefaces) menu item leads to an IllegalStateException

13,769

Solution 1

EL (read: reflection) cannot access/construct anonymous classes. Refactor them into fullworthy classes.

So, replace

    item.addActionListener(new ActionListener() {
        @Override
        public void processAction(ActionEvent event)
                throws AbortProcessingException {
            System.out.println(event.toString());
        }
    });

by

    item.addActionListener(new FooActionListener());

and

public class FooActionListener implements ActionListener {

    @Override
    public void processAction(ActionEvent event)
            throws AbortProcessingException {
        System.out.println(event.toString());
    }

}

See also:

Solution 2

It looks like an additional restriction is that the ActionListener class have no contructor arguments, which kind of adds insult to injury here. As far as I can see the addActionListener probably just stores the class name of the object passed to it.

In fact if the intent was to make this listener unusable by preventing any data being passed to the listener from your backing bean they could hardly have done more.

You get another IllegalStateException if you try subclassing MenuItem.

You can't pass an object containing data to MenuItem as a value, it requires a String.

It doesn't seem to allow the listener as an inner class.

But I think I may have cracked it, by putting the needed data in the attributes map of the menu item.

Here's what I wound up with:

public class MenuSelectListener implements ActionListener {
public static final String MENU_ACTION_KEY = "menu.action.delegate";

private final static Log log = LogFactory.getLog(MenuSelectListener.class);

@Override
public void processAction(ActionEvent ae) throws AbortProcessingException {
    System.out.println("listener invoked");
    if (ae.getComponent() instanceof MenuItem) {
        Runnable delegate = (Runnable) ae.getComponent().getAttributes().get(MENU_ACTION_KEY);
        if(delegate != null)
            delegate.run();
        else
            log.error("Menu action has no runnable");
    } else {
        log.error("Listener, wrong component class: " + ae.getComponent().getClass().getName());
    }
}

}

To set up an item:-

        item.setValue("Cancel");
        sm.getChildren().add(item);
        item.addActionListener(new MenuSelectListener());
        item.getAttributes().put(MenuSelectListener.MENU_ACTION_KEY, new MiscActionDelegate(MiscActions.close));

With:

private class MiscActionDelegate implements Runnable, Serializable {

(works as an inner class, but cannot be anonymous).

Share:
13,769

Related videos on Youtube

Lars Blumberg
Author by

Lars Blumberg

All-round software writer, in particular interested in writing web (Python/ReactJS) and mobile (Android/Java/Kotlin, iOS/ObjC/Swift/Capacitor). In the past I wrote native desktop applications (Windows, macOS) as well.

Updated on June 04, 2022

Comments

  • Lars Blumberg
    Lars Blumberg almost 2 years

    In JSF backed bean I got an IllegalStateException when the programmatically added action listener of a programmatically added Primefaces menu item is called. I tried both request and session scope but both are leading to the same error. Obviously there's need -- according to the stack trace -- to restore the view when an action listener is executed and I let my ToolbarBean implement Serializable with no different effect. What should I consider in order to get this to work?

    User interface definition

    <?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"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:p="http://primefaces.prime.com.tr/ui">
    
    <h:head>
        <title>TITLE</title>
    </h:head>
    
    <h:body>
        <h:form>
            <p:menu model="#{toolbarBean.model}" type="tiered" />
        </h:form>
    </h:body>
    </html>
    

    Backed bean providing the menu

    @Named
    @Scope("request")
    public class ToolbarBean implements Serializable {
    
        private static final long serialVersionUID = -8556751897482662530L;
    
        public ToolbarBean() {
            model = new DefaultMenuModel();
    
            MenuItem item;
    
            // Direct menu item
            item = new MenuItem();
            item.setValue("Menuitem 1");
            item.addActionListener(new ActionListener() {
                @Override
                public void processAction(ActionEvent event)
                        throws AbortProcessingException {
                    System.out.println(event.toString());
                }
            });
    
            model.addMenuItem(item);
    
            item = new MenuItem();
            item.setValue("Menuitem 2");
            item.addActionListener(new ActionListener() {
                @Override
                public void processAction(ActionEvent event)
                        throws AbortProcessingException {
                    System.out.println(event.toString());
                }
            });
    
            model.addMenuItem(item);
        }
    
        private MenuModel model;
    
        public MenuModel getModel() {
            return model;
        }
    }
    

    Exception when clicking one of the menu buttons

    javax.faces.FacesException: java.lang.IllegalStateException: java.lang.InstantiationException: id.co.sofcograha.baseui.ToolbarBean$1
        at javax.faces.component.UIComponent.invokeOnComponent(UIComponent.java:1284)
        at javax.faces.component.UIComponentBase.invokeOnComponent(UIComponentBase.java:673)
        at javax.faces.component.UIComponent.invokeOnComponent(UIComponent.java:1290)
        at javax.faces.component.UIComponentBase.invokeOnComponent(UIComponentBase.java:673)
        at javax.faces.component.UIComponent.invokeOnComponent(UIComponent.java:1290)
        at javax.faces.component.UIComponentBase.invokeOnComponent(UIComponentBase.java:673)
        at javax.faces.component.UIComponent.invokeOnComponent(UIComponent.java:1290)
        at javax.faces.component.UIComponentBase.invokeOnComponent(UIComponentBase.java:673)
        at com.sun.faces.application.view.StateManagementStrategyImpl.restoreView(StateManagementStrategyImpl.java:297)
        at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:177)
        at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:119)
        at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:438)
        at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:144)
        at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:284)
        at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:182)
        at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
        at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:107)
        at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
    
  • Lars Blumberg
    Lars Blumberg about 13 years
    Thank you Balus. Please say hello to Chichiray for me.
  • BalusC
    BalusC over 12 years
    No, that isn't the cause. It's an interface (and unconstructable anyway) and the OP has instantiated it as an anonymous class instead of a normal implementation class.
  • Malcolm McMaho
    Malcolm McMaho over 12 years
    Yes, but I tried doing it as suggested, but with a class which took constructor args, and the IllegalStateException still happened.