开发者

Dynamically loading JSF beans in a Facelet template

开发者 https://www.devze.com 2023-02-01 20:29 出处:网络
Im having a problem dynamically loading beans in my program. I have my facelet template like this: <ui:define name=\"content\">

Im having a problem dynamically loading beans in my program. I have my facelet template like this:

<ui:define name="content">
        <f:loadBundle var="Application" basename="start" />
        <f:loadBundle var="Messages" basename="#{ApplicationController.bundleName}" />
        <h:panelGroup rendered="#{ApplicationController.chosenBean.hasAccess}">
            <h:form>
                <h:outputText value="Error: #{ApplicationController.chosenBean.errorMessage}" id="outputErrorMessage2" /><br />
                  <h:outputText value="Report running: #{ApplicationController.chosenBean.runningReport}" /><br />
                  <ui:insert name="application-content" />
                  <h:commandButton action="#{ApplicationController.chosenBean.actionRunReport}" value="Create report"/>
               </h:form>
            </h:panelGroup>
            <h:panelGroup rendered="#{!ApplicationController.chosenBean.hasAccess}">
            <h:outputText value="Err" />
        </h:panelGroup>
</ui:define>

As you can see I want the ApplicationController to decide which Javabean to use. This is done with the following code:

 ApplicationController.java
public ReportWeb getChosenBean() {
    String reportName = getReportNameFromServletPath();
    //Make first character upper case
    String beanName = reportName.substring(0, 1).toUpperCase() + reportName.substring(1);
    logger.log(Level.INFO, "Loading bean with name : {0}", BEAN_PACKET_NAME + beanName);
    try {
        if (Class.forName(BEAN_PACKET_NAME + beanName).newInstance() instanceof ReportWeb) {
            chosenBean = (ReportWeb) Class.forName(BEAN_PACKET_NAME + beanName).newInstance();
        }
    } catch (ClassNotFoundException ex) {
        Logger.getLogger(ApplicationController.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        Logger.getLogger(ApplicationController.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        Logger.getLogger(ApplicationController.class.getName()).log(Level.SEVERE, null, ex);
    }
    return chosenBean;开发者_开发问答
}

All my Javabeans subclass the ReportWeb class, which is an abstract class containing access logic and other convenience methods. The most important method is the

public abstract String actionRunReport();

All my reports implement this one. However in my template I cannot call this method by doing this:

<h:commandButton action="#{ApplicationController.chosenBean.actionRunReport}" value="Create report"/>

I recieve no error message, but it just does not work. I have to hardcode the Javabean name like this:

<h:commandButton action="#{AntallHenvendelser.actionRunReport}" value="Create report"/>

Does anoyne know why my technique is not working?

EDIT: The method is never called from the action button that dynamically loads the bean.


The root cause is likely because the rendered attribute of the action component (or at least one of its parents) has evaluated false during the request of the form submit. This way the action won't be invoked. You need to ensure that the rendered attribute doesn't evaluate false while the request of form submit is to be processed.

See also this answer for an overview of possible causes of an action component not being invoked.


Regardless, the approach as you've shown as far is plain inefficient. I'd make the ApplicationController request scoped and load the chosen bean as follows:

public class ApplicationController {

    private ReportWeb chosenBean;

    public ApplicationController() {
        chosenBean = findBean(getReportNameFromServletPath(), ReportWeb.class);
    }

    public static <T> T findBean(String managedBeanName, Class<T> beanClass) {
        FacesContext context = FacesContext.getCurrentInstance();
        return beanClass.cast(context.getApplication().evaluateExpressionGet(context, "#{" + managedBeanName + "}", beanClass);
    }

    // ...
}

This way it is loaded only once per request instead of at least once per EL expression which is calling the getter (which can be at least twice as much when used inside rendered attribute).

This way you're also grabbing the bean which is actually managed by JSF instead of creating it (twice!) manually inside a getter. If JSF has already created an instance, exactly this one will be returned. If JSF hasn't already created it, a new one will be created and put in the scope accordingly before it's returned.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号