Frage

In an effort to remove 'static' declarations throughout my app (to help JVM's GC), I removed 'static' from the definition of the Converter class below. So that resulted in the infamous error below:

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)

I assume 'static' is necessary, because only one copy of the class is created per application. Correct? So, can I define the class as a @Singleton @Lock(READ) to resolve the issue?

Per NetBeans generated JSF controller/bean code, the Converter is usually defined in the same .java file as the controller or @ManagedBean. Honestly, I do 'not' want to use addConverter() or converterId="..." in the xhtml. I prefer to use @FacesConverter, since this has been working for me throughout the app.

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());
            }
        }
    }
}

Please advise.

ALSO, please let me know how to get email notifications whenever someone responds to my questions here on stackoverflow.com.

Thanks!

War es hilfreich?

Lösung

The answer is YES, with a little help from JNDI lookup.

I just removed the CustomerControllerConverter from the JSF @RequestScoped CustomerController, created the converter class, marked it with @Singleton @Lock(READ), and referenced the @Stateless EJB via JNDI lookup. See below.

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());
        }
    }
}

LESSON LEARNED: ejbFacade could NOT be referenced or instantiated via the following:

@Inject, @EJB, BeanManager

References:

Oracle blogs

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

TomEE JavaEE Examples - Referencing EJBs

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

Ultimately, I had to refer to the server.log file of Glassfish, the Reference Implementation (RI), to see a sample of JNDI lookup path for my @Stateless EJB's, since I used Glassfish, in the past. :)

UPDATE:

You can also find the portable global JNDI name in TomEE/catalina log. It will look like this, below.

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)

where 'webApp' in the global JNDI name may be the name of your WAR filename or possibly EJB JAR filename, etc...

Hope this helps others!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top