20

I have an EJB (PersonManager) in the Enterprise Application modul, which injects another EJB (Person):

@Stateful
public class PersonManager implements PersonManagerLocal {
    @EJB
    private PersonLocal person;

    @Override
    public void setPersonName(String name) {
        person.setName(name);
    }

    @Override
    public String getPersonName() {
        return person.getName();
    }
}

I want to use the PersonManager EJB in a JSF web app. I define it in the faces-config.xml:

<managed-bean>
    <managed-bean-name>personManager</managed-bean-name>
    <managed-bean-class>ejb.PersonManager</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

The problem is that, the injection of the PersonLocal EJB doesn't happen. The person property is always NULL. What did I wrong?

But if I inject the PersonManager in a JSF managed bean like this:

@ManagedBean
@RequestScoped
public class Index {
    @EJB
    private PersonManagerLocal personManager;
    ....

IT WORKS. I need the first scenario, please help me :-D

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Forty
  • 420
  • 1
  • 5
  • 10

1 Answers1

42

You are mixing the responsibilities of EJBs and JSF managed beans. The faces-config.xml registers only JSF artifacts, such as managed beans and not EJBs. Your registration in faces-config.xml

<managed-bean>
    <managed-bean-name>personManager</managed-bean-name>
    <managed-bean-class>ejb.PersonManager</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

does basically exactly the same as

@ManagedBean
@SessionScoped
public class PersonManager {
    // ...
}

In other words, you're registering the class as a JSF managed bean which is available in views by #{personManager}. This does not represent the same instance as is managed by the EJB container. You can and should not use faces-config.xml to register EJBs. There you use the annotations from the javax.ejb package for, such as @Stateless and @Stateful. That's all you need to register an EJB.

Actually, registering JSF managed beans in faces-config.xml is an old JSF 1.x way which has in JSF 2.x been replaced by the new @ManagedBean annotation.


Update the proper approach would be:

View (the Facelets file):

<h:form>
    <h:inputText value="#{personManager.person.name}" />
    ...
    <h:commandButton value="Save" action="#{personManager.save}" />
    <h:messages />
</h:form>

Controller (the JSF managed bean):

@ManagedBean
@ViewScoped
public class PersonManager implements Serializable {

    private Person person;

    @EJB
    private PersonService personService;

    @PostConstruct
    public void init() {
        person = new Person();
    }

    public void save() {
        personService.create(person);
        FacesContext.getCurrentInstance().addMessage(null,
            new FacesMessage("Person successfully created, new ID is " + person.getId()));
    }

    // ...
}

Model (the JPA entity):

@Entity
public class Person implements Serializable {

    @Id
    private Long id;

    private String name;

    // ...
}

Service (the stateless EJB):

@Stateless
public class PersonService {

    @PersistenceContext
    private EntityManager em;

    public void create(Person person) {
        em.persist(person);
    }

    // ...
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks, the PersonManager is a business EJB, therefore not contains the @ManagedBean annotation. It is created by the JSF engine, just not injects the person EJB into it, under the faces-config scenario. – Forty Dec 25 '11 at 07:23
  • I know what EJBs are and how they are supposed to be used. Do you? What part exactly is now still unclear? – BalusC Dec 25 '11 at 12:42
  • 4
    Please note that the `@Stateful` is missing in my answer example. It's **not** an EJB this way. You were attempting to abuse the EJB as a JSF managed bean. This is not right. You should basically have 1 XHTML file as View, 1 JSF managed bean as Controller, 1 entity as Model and 1 EJB as service. In your question, the `PersonManager` should be a JSF managed bean, not an EJB. The `PersonLocal` seems too much an entity, not an EJB. So I doubt if it really needs to be an EJB. EJBs are intented to deliver services like `create()`, `save()`, etc. – BalusC Dec 25 '11 at 16:29
  • See also the updated answer for a kickoff example. Please note that you don't need config xml files at all. – BalusC Dec 25 '11 at 16:40
  • Thanks :-D It's clear now. I can just do that as in the second scenario (injecting the EJB into a JSF managed bean), because PersonManager is an EJB, and can't be act as a JSF managed bean. Am I correct? – Forty Dec 25 '11 at 16:49
  • @BalusC, if the `PersonManager` in your example happened to be `@SessionScoped` instead of `@ViewScoped`, should `PersonService` be looked up via InitialContext instead of being injected? – Patrick Garner Feb 21 '12 at 07:46
  • 2
    I was reading the answer and at the same time I was admiring the author, but when I reached the end my excitement decreased by seeing your name "BalacC" at the bottom. No wonder only you can write such nice answers. Good luck man – ehsun7b Apr 20 '13 at 05:43
  • @BalusC: May I ask the example you have written as an answer, is it possible to put all (view, controller and service) in single maven web project? Packaged as a *.war file? – DaeYoung Aug 10 '18 at 01:27
  • @BalusC Don't we have to create an interface for EJBs...? for eg: in above case: `@Stateless public class PersonService implements PersonServiceInterface{ ... }` and we inject PersonServiceInterface inside ManagedBean..? – paawan Oct 27 '20 at 02:10
  • @Paawan12: nope. It's not necessary anymore since EJB 3.0 which was released already more than a decade ago. – BalusC Oct 27 '20 at 09:35
  • @BalusC So Is it a good design pattern to directly inject EJBs instead of it's interface..? – paawan Oct 28 '20 at 11:03
  • @Paawan12: If you have only one impl, then yes it's completely acceptable. – BalusC Oct 28 '20 at 11:05