【问题标题】:Can Converter @FacesConverter(forClass=[someClass.class]) be implemented as @Singleton @Lock(READ)?Converter @FacesConverter(forClass=[someClass.class]) 可以实现为@Singleton @Lock(READ) 吗?
【发布时间】:2013-04-13 11:50:41
【问题描述】:

为了在我的应用程序中删除“静态”声明(以帮助 JVM 的 GC),我从下面的 Converter 类的定义中删除了“静态”。这导致了下面臭名昭著的错误:

Apr 13, 2013 4:10:38 AM org.apache.myfaces.application.ApplicationImpl internalCreateConverter
SEVERE: Could not instantiate converter jsf.CustomerController$CustomerControllerConverter
java.lang.InstantiationException: jsf.CustomerController$CustomerControllerConverter
    at java.lang.Class.newInstance0(Unknown Source)
    at java.lang.Class.newInstance(Unknown Source)
    at org.apache.myfaces.application.ApplicationImpl.internalCreateConverter(ApplicationImpl.java:1626)
    at org.apache.myfaces.application.ApplicationImpl.createConverter(ApplicationImpl.java:1545)
    at javax.faces.application.ApplicationWrapper.createConverter(ApplicationWrapper.java:158)

我认为“静态”是必要的,因为每个应用程序只创建一个类的副本。正确的?那么,我可以将类定义为 @Singleton @Lock(READ) 来解决问题吗?

根据 NetBeans 生成的 JSF 控制器/bean 代码,转换器通常定义在与控制器或 @ManagedBean 相同的 .java 文件中。老实说,我“不想”在 xhtml 中使用 addConverter() 或 converterId="..." 。我更喜欢使用@FacesConverter,因为它在整个应用程序中都对我有用。

package jsf;

import jpa.entities.Customer;
import jpa.session.CustomerFacade;

import java.io.Serializable;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

@ManagedBean(name = "customerController")
@RequestScoped
public class CustomerController implements Serializable {

    @EJB
    private jpa.session.CustomerFacade ejbFacade;

    public CustomerController() {
    }

    @FacesConverter(forClass = Customer.class)
    public class CustomerControllerConverter implements Converter {

        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            /*
             * 2012-07-10 when user enters invalid/incomplete value (e.g. "irene", see below) in AutoComplete
             *
            WARNING: For input string: "irene"
            java.lang.NumberFormatException: For input string: "irene"
                    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
                    at java.lang.Integer.parseInt(Integer.java:492)
                    at java.lang.Integer.valueOf(Integer.java:582)
                    at jsf.pointOfContact.pf_PointOfContactController$PointOfContactControllerConverter.getKey(pf_PointOfContactController.java:1625)
                    at jsf.pointOfContact.pf_PointOfContactController$PointOfContactControllerConverter.getAsObject(pf_PointOfContactController.java:1620)
                    at org.primefaces.component.autocomplete.AutoCompleteRenderer.getConvertedValue(AutoCompleteRenderer.java:529)
                    at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030)
                    at javax.faces.component.UIInput.validate(UIInput.java:960)
             *
             */
            try {
                Integer test = getKey(value);
            } catch (java.lang.NumberFormatException e) {
                return null;
            }
            CustomerController controller = (CustomerController) facesContext.getApplication().getELResolver().
                    getValue(facesContext.getELContext(), null, "customerController");
            return controller.ejbFacade.find(getKey(value));
        }

        java.lang.Integer getKey(String value) {
            java.lang.Integer key;
            key = Integer.valueOf(value);
            return key;
        }

        String getStringKey(java.lang.Integer value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }

        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof Customer) {
                Customer o = (Customer) object;
                return getStringKey(o.getCustomerId());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + Customer.class.getName());
            }
        }
    }
}

请指教。

另外,当有人在 stackoverflow.com 上回答我的问题时,请告诉我如何接收电子邮件通知。

谢谢!

【问题讨论】:

    标签: jakarta-ee singleton converter openejb apache-tomee


    【解决方案1】:

    答案是肯定的,在 JNDI 查找的帮助下。

    我刚刚从 JSF @RequestScoped CustomerController 中删除了 CustomerControllerConverter,创建了转换器类,用 @Singleton @Lock(READ) 标记它,并通过 JNDI 查找引用了 @Stateless EJB。见下文。

    package converter;
    
    import java.util.concurrent.TimeUnit;
    
    import javax.ejb.AccessTimeout;
    import javax.ejb.Lock;
    import javax.ejb.LockType;
    import javax.ejb.Singleton;
    
    import javax.faces.component.UIComponent;
    import javax.faces.context.FacesContext;
    import javax.faces.convert.Converter;
    import javax.faces.convert.FacesConverter;
    
    import javax.naming.InitialContext;
    
    import jpa.entities.Customer;
    import jpa.session.CustomerFacade;
    
    
    /**
     *
     * @author Administrator
     */
    @Singleton
    @Lock(LockType.READ)
    @AccessTimeout(value = 1, unit = TimeUnit.MINUTES)
    @FacesConverter(forClass = Customer.class)
    public class CustomerConverter implements Converter {
    
        public CustomerConverter() {
    
        }
    
        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            /*
             * 2012-07-10 when user enters invalid/incomplete value (e.g. "irene", see below) in AutoComplete
             *
            WARNING: For input string: "irene"
            java.lang.NumberFormatException: For input string: "irene"
                    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
                    at java.lang.Integer.parseInt(Integer.java:492)
                    at java.lang.Integer.valueOf(Integer.java:582)
                    ...
                    ...
                    at org.primefaces.component.autocomplete.AutoCompleteRenderer.getConvertedValue(AutoCompleteRenderer.java:529)
                    at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030)
                    at javax.faces.component.UIInput.validate(UIInput.java:960)
             *
             */
            try {
                Integer test = getKey(value);
            } catch (java.lang.NumberFormatException e) {
                return null;
            }
            Object object = null;
            CustomerFacade ejbFacade;
            try {
                InitialContext ic = new InitialContext();
                ejbFacade = (CustomerFacade) ic.lookup("java:global/appWARFileNameOrAppContextName/CustomerFacade");
                if (ejbFacade == null) {
                    System.err.println("CustomerConverter.getAsObject(): ejbFacade = null)");
                    return null;
                }
            } catch (Exception e) {
                System.err.println("CustomerConverter.getAsObject(): error on JNDI lookup of CustomerFacade");
                e.printStackTrace();
                return null;
            }
            try {
                object = ejbFacade.find(getKey(value));
            } catch (Exception e) {
                System.err.println("CustomerConverter.getAsObject(): error on ejbFacade.find(getKey(value))");
                e.printStackTrace();
                return null;
            }
            return object;
        }
    
        java.lang.Integer getKey(String value) {
            java.lang.Integer key;
            key = Integer.valueOf(value);
            return key;
        }
    
        String getStringKey(java.lang.Integer value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }
    
        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof Customer) {
                Customer o = (Customer) object;
                return getStringKey(o.getCustomerId());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + Customer.class.getName());
            }
        }
    }
    

    经验教训:无法通过以下方式引用或实例化 ejbFacade:

    @注入, @EJB, 豆类管理器

    参考资料:

    Oracle 博客

    1. Ken Saks's Blog: Application-specified Portable JNDI Names
    2. Ken Saks's Blog: Portable Global JNDI Names

    TomEE JavaEE 示例 - 引用 EJB

    1. Injection Of Ejbs
    2. Lookup Of Ejbs with Descriptor
    3. Lookup Of Ejbs

    最后,我不得不参考 Glassfish 的 server.log 文件,即参考实现 (RI),以查看我的 @Stateless EJB 的 JNDI 查找路径示例,因为我过去使用过 Glassfish。 :)

    更新:

    您还可以在 TomEE/catalina 日志中找到可移植的全局 JNDI 名称。如下所示。

    Apr 13, 2013 10:00:58 AM org.apache.openejb.assembler.classic.JndiBuilder bind
    INFO: Jndi(name=CustomerFacadeLocalBean) --> Ejb(deployment-id=CustomerFacade)
    
    Apr 13, 2013 10:00:58 AM org.apache.openejb.assembler.classic.JndiBuilder bind
    INFO: Jndi(name=global/webApp/CustomerFacade!jpa.session.CustomerFacade) --> Ejb(deployment-id=CustomerFacade)
    
    Apr 13, 2013 10:00:58 AM org.apache.openejb.assembler.classic.JndiBuilder bind
    INFO: Jndi(name=global/webApp/CustomerFacade) --> Ejb(deployment-id=CustomerFacade)
    

    全局 JNDI 名称中的“webApp”可能是您的 WAR 文件名的名称,也可能是 EJB JAR 文件名等...

    希望这对其他人有所帮助!

    【讨论】:

      猜你喜欢
      • 2015-10-14
      • 1970-01-01
      • 2012-04-30
      • 2011-11-18
      • 1970-01-01
      • 2012-10-22
      • 1970-01-01
      • 2013-09-30
      • 1970-01-01
      相关资源
      最近更新 更多