6 Business Rules the World – EJB

In this chapter, we address two main issues of our software project. First, we improve the software architecture by introducing separate layers for controller logic, business logic and data storage. Second, we implement the business logic and the data storage of our application with Java EE’s traditional and designated bean technology, namely Enterprise JavaBeans (EJB).

Before we start, please verify again that your project is accessable in Codenvy and is up to date, i.e. you finished the previous chapters successfully as described. If this is not the case you can follow the preceeding factory link that generates a project environment for you[1].

Project after Chapter 5 (JSF)

Project after Chapter 5 (JSF)


6.1 Software Architecture’s Review

Looking at our software architecture right now, we realize that the Controller Bean (MemoController.java) does not only realize the controller logic of our application. Additionally, it provides  the business logic (setting the current time to a new memo) and works as a data storage (list of memos is a property of the bean) which injures the design principle Separation of Concerns (SoC). In order to simplify development, extendability and maintenance, it is beneficial to separate concerns, like view, controller logic, business logic or data storage. The better we follow this principle, the more probable it is that we can later improve or modify one part of code without any influence to the others.

6.1.1 Bean Classes and their Concerns

In order to realize a well-separated architecture, we introduce two additional bean classes. One for business logic (MemoServiceBean.java) and one for data storage (MemoStore.java). Tab. 6-1 gives a short summary of beans and their responsibilities.

Bean Class Concerns
MemoStore  The bean contains the list of memos. It provides methods for adding a memo to the list and for accessing or deleting the whole list.
MemoServiceBean  The bean encapsulates a MemoStore bean. Since our application is very simple, it provides methods for accessing memos by forwarding the method call to the MemoStore bean. The only business logic is to add the date property of a new memo.
MemoController  The bean stores the current memo of a user in the user’s session. Additionally, it encapsulates a MemoServiceBean for getting access to the business methods. The controller bean itself provides action methods for adding a memo or deleting the list memos that are called by the view. Further, it provides a method for retrieving the current list of memos. All methods of the controller bean forward the call to the respective business method of the MemoServiceBean and passes the current memo object if necessary.

Tab. 6-1     Bean classes and their concerns.

6.1.2 The Data Storage Bean

We start to implement the MemoStore bean class (see Listing 6-1). By that, we also introduce a new package named press.turngeek.mymemo.data. 

package press.turngeek.mymemo.data;

import java.util.List;
import java.util.LinkedList;

import press.turngeek.mymemo.model.Memo;

public class MemoStore {

    private List<Memo> memos;

    public MemoStore() {
        super();
        memos = new LinkedList<Memo>();
    }

    public List<Memo> findAll() {
        return memos;
    }

    public void persist(Memo memo) {
        memos.add(memo);
    }

    public void removeAll() {
        memos.clear();
    }
}

Listing 6-1 MemoStore class

As you can see in Listing 6-1, the class MemoStore is just a container class for the list of memos. That is the only concern of the class.

6.1.3 The Business Logic Bean

In Listing 6-2, we can see the class MemoServiceBean realizing business logic. Together with the class, we introduce another new package press.turngeek.mymemo.service.

package press.turngeek.mymemo.service;

import java.util.Date;
import java.util.List;

import press.turngeek.mymemo.model.Memo;
import press.turngeek.mymemo.data.MemoStore;

public class MemoServiceBean {

    private MemoStore memoStore;

    public List<Memo> getAllMemos() {
        return memoStore.findAll();
    }

    public void addMemo(Memo memo) {
        Memo newMemo = new Memo();
        newMemo.setDescription(memo.getDescription());
        newMemo.setCreated(new Date());
        memoStore.persist(newMemo);
    }

    public void resetMemos() {
        memoStore.removeAll();
    }
}

Listing 6-2 MemoServiceBean class

Looking at Listing 6-2, we see that the only method addMemo adds (some kind of) business logic to the implementation. It creates a new Memo object, copies the description from the passed object and sets the current date. This new Memo object is passed to the MemoStore bean. Hence, the only concern of the MemoServiceBean is to encapsulate the MemoStorage and to add business logic if necessary. This might sound overengineered, but there is another reason why it makes sense to introduce the business layer. One further aspect of business logic is that it normally has to be executed transactionally, because several systems or databases are involved. In case of failure, business logic needs a roll-back mechanism that should be provided by the application server. We’ll come back to this point in chapter 6.2.

6.1.4 The Controller Bean

Since a lot of code moved from the last version of the class MemoController to the two introduced bean classes, the resulting controller bean is rather lean (see Listing 6-3).

package press.turngeek.mymemo.controller;

import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

import javax.faces.event.ActionEvent;

import press.turngeek.mymemo.model.Memo;
import press.turngeek.mymemo.service.MemoServiceBean;

@SessionScoped
@ManagedBean(name = "memoController")
public class MemoController {

    private Memo memo;

    private MemoServiceBean memoService;

    public MemoController() {
        memo = new Memo();
    }

    public Memo getMemo() {
        return memo;
    }

    public void setMemo(Memo memo) {
        this.memo = memo;
    }

    public void doAdd(ActionEvent event) {
        memoService.addMemo(memo);
        memo.setDescription("");
    }

    public void doReset(ActionEvent event) {
        memoService.resetMemos();
    }

    public List<Memo> getMemos() {
        return memoService.getAllMemos();
    }
}

Listing 6-3 MemoController class

The main difference to its former version is that the MemoController class in Listing 6-3 encapsulates and uses a bean of the class MemoServiceBean instead of implementing everything on its own. The only concern of the class is to provide action listener and data retrieval methods for the view, to realize navigation (since we only have one view, we don’t need any navigation) and to store data entered by the user and pass it to the business layer if demanded[2].

6.1.5 Let’s Go!

Stop! Probably, you’ve understood every step explained above and can’t wait to try out the behavior of our application. Unfortunatelly, it will not work at all. But why?

As we already know, the class MemoController is a Managed Bean. The life cycle is managed by the application server. But who manages the life cycle of MemoServiceBean and MemoStore? The answer is: no one!

We didn’t use any annotation that declares these classes as a certain kind of bean managed in a certain way by the application server. Further, we declared has-a relations between MemoController and MemoServiceBean as well as MemoServiceBean and MemoStorage. But we have never created an object of these types and initialized the respective properties. The only bean that is managed is the controller bean. It has a property of type MemoServiceBean that is initialized with null by the default constructor of the MemoController class. This will lead to a NullPointerException during the usage of the application!

What can we do? Of course, we can turn both bean classes to managed beans, too. By adding the respective annotations (@ManagedBean, @SessionScoped) to the class declaration, the application server would recognize them as managed beans. But one problem remains. How can we force the application server to create new beans and to store the references in the respective bean’s properties? In general, this mechanism is called Dependency Injection (DI).

Dependency Injection (DI)
is a pattern that transfers the tasks of object creation, deployment, and management from the user of the object to the runtime environment (container). The user defines only what they wish to have at each point (type, scope, number), and the runtime environment takes care of everything else. This simplifies access, enables loose coupling between objects and ensures that type safety is preserved.

JSF Managed Beans provide DI (Annotation @ManagedProperty), but at this point we don’t want to go into deep, because JSF Managed Beans are not the appropriate bean type for implementing business logic (e.g. no transactions!). Instead, we use EJBs.

6.2 Enterprise JavaBeans

Enterprise JavaBeans (EJB) was the first and is still the most used server-side component technology of Java EE for implementing business logic in enterprise applications. EJB addresses typical issues in enterprise applications such as security and transactions. Since there is a need in every enterprise application for securing access to business logic and to implement it transactionally, EJB provides these mechanisms out-of-the-box. The developer can rely on these standard mechanisms and can concentrate on realizing the business logic itself, rather than implementing a proprietary security or transaction concept for each enterprise application. Like JSF Managed Beans, EJBs are managed, too. While JSF Managed Beans are managed by a so called web container within the application server, the life cycle of EJBs is managed by a business container or EJB container. EJB supports Dependency Injection which simplifies their usage within the application (Annotation @EJB). Tab. 6-2 shows the different types of EJBs.

EJB Type Description
Stateless Session Bean (SSB)  Beans using a SSB can use its functionality, but the interaction is stateless. That means, no data can be stored in the SSB between subsequent method calls. The EJB container either manages a pool of SSBs and dispatches method calls to it or creates a new instance for each request.
Stateful Session Bean (SFSB)  Beans using a SFSB get it exclusivly. The interaction is stateful which means that data can be stored over several subsequent method calls to the SFSB. Each client bean has its own SFSB.
Singleton  A Singleton bean exists only once in the EJB container. Beans using it work on the same instance and data stored in the singleton is shared among the beans.

Tab. 6-2     Types of EJBs

6.2.1 Using EJB in My-Memo

Next, we want to apply EJB technology to our application. The question is which type of EJB is appropriate to the classes MemoServiceBean and MemoStore. Looking at the service bean, we realize that we do not store any data representing the state of communication. Hence, we should use a SSB, so that the application server can spare memory resources by load balancing a potential large amount of service calls, at the same time, on the SSB pool. In contrast, the class MemoStore represents a layer that simulates the access to a global database. Therefore, the use of a Singleton EJB seems to be the appropriate choice[3].

It is very easy to turn a class into a certain type of EJB. For this reason, the API provides annotations for each type in the package javax.ejb. Let us start with the class MemoStore. In order to turn it into a Singleton EJB, we just add the annotation javax.ejb.Singleton at the class level. That’s all.

package press.turngeek.mymemo.data;

import java.util.List;
import java.util.LinkedList;

import javax.ejb.Singleton;

import press.turngeek.mymemo.model.Memo;

@Singleton
public class MemoStore {

    private List<Memo> memos;

    public MemoStore() {
        super();
        memos = new LinkedList<Memo>();
    }

    public List<Memo> findAll() {
        return memos;
    }

    public void persist(Memo memo) {
        memos.add(memo);
    }

    public void removeAll() {
        memos.clear();
    }
}

Listing 6-4 MemoStore class – as EJB Singleton.

In order to turn our class MemoServiceBean into a SSB, we just add the Annotation javax.ejb.Stateless at the class level. As we know from Listing 6-1, MemoServiceBean encapsulates our Singleton MemoStore. We can activate DI for the corresponding attribute by annotating it with javax.ejb.EJB. At runtime the EJB container provides the attribute with a reference to the Singleton.

package press.turngeek.mymemo.service;

import java.util.Date;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;

import press.turngeek.mymemo.model.Memo;
import press.turngeek.mymemo.data.MemoStore;

@Stateless
public class MemoServiceBean {

    @EJB
    private MemoStore memoStore;

    public List<Memo> getAllMemos() {
        List<Memo> memos = memoStore.findAll();
        return memos;
    }

    public void addMemo(Memo memo) {
        Memo newMemo = new Memo();
        newMemo.setDescription(memo.getDescription());
        newMemo.setCreated(new Date());
        memoStore.persist(newMemo);
    }

    public void resetMemos() {
        memoStore.removeAll();
    }
}

Listing 6-5 MemoServiceBean class – as SSB.

6.2.2 Integrating EJBs in Managed Beans

In the last section, we saw how we relate two EJBs by using DI with the annotation @EJB. This mechanism also works for relating other bean types with EJBs. In order to complete this iteration of our application, we have to relate our controller bean with the service bean. This can be done by annotating the attribute memoService with @EJB in the class MemoController.

package press.turngeek.mymemo.controller;

import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.ejb.EJB;

import javax.faces.event.ActionEvent;

import press.turngeek.mymemo.model.Memo;
import press.turngeek.mymemo.service.MemoServiceBean;

@SessionScoped
@ManagedBean(name = "memoController")
public class MemoController {

    private Memo memo;

    @EJB
    private MemoServiceBean memoService;

    public MemoController() {
        memo = new Memo();
    }

    public Memo getMemo() {
        return memo;
    }

    public void setMemo(Memo memo) {
        this.memo = memo;
    }

    public void doSave(ActionEvent event) {
        memoService.addMemo(memo);
        memo.setDescription("");
    }

    public void doReset(ActionEvent event) {
        memoService.resetMemos();
    }

    public List<Memo> getMemos() {
        return memoService.getAllMemos();
    }
}

Listing 6-6 MemoController class – Injection of a SSB.

Now, the application is executable again. Run the application and check if everything works fine!

6.2.3 Conclusions

We started the section by explaining that EJB provides helpful properties for implementing business logic, especially mechanisms for security and transactions. But if we look at our code we can not see any use of such concepts. Concerning security, the reason is that we don’t realize a secure application. We neither use authentication nor authorization and, therefore, it is obvious that there is nothing visible in the code. Although, we do not see any usage of transaction management in the code and we do not really need transactions for our methods since we do not access external systems like databases, our EJBs execute each method in a new transaction. That is the default behavior of EJBs! Of course, there are possibilities of configuring a different transactional behavior if required. For our purpose, the default behavior is (more than) sufficient and so we don’t need any changes in the code. In general, this follows one of the design paradigms of Java EE, namely Convention over Configuration (CoC). That means that only unconventional behavior has to be configured or coded which keeps the code and configuration simple as long as the default behavior is sufficient.

Finally, we want to revisit our discussion about the choice of the EJB type for our classes MemoServiceBean and MemoStore from section 6.2.1. You might have already realized that the behavior of our current implementation differs slightly from that of the previous section. Since we store our memos in a Singleton, all memos are global which means that the data is independent of the session. You can easily reproduce this behaviour by accessing the application with different browsers at the same time (some browsers also support the creation of multiple sessions for one application). Although the behaviour is not equivilent to that of the previous chapter, we claim that this is the right decision for this iteration.

  • The main problem is that EJB knows nothing about the web context in which they are called, e.g. the scope of a request, since they were designed to work in web applications as well as in application client scenarios. They are like a service layer in a Java architecture providing business logic. A conversation between a client and an EJB can be stateless or stateful, but the client is just another Java object. Hence the life cycle of the EJB depends on the life cycle of this Java object and not on that of a web scope.
  • The only alternative to reach the same behaviour  as in the previous chapters, is to connect the EJB indirectly with the web scope. The controller bean belongs to the session scope of the web application. If we inject the MemoServiceBean as a SFSB, we bind the life time of the service bean to that of the controller bean. Further, we could also inject the MemoStore bean as a SFSB in the MemoServiceBean with the same result. Now, the application would behave as in the chapters before, since the EJBs are bound statefully to the session scoped controller. If the session terminates, the controller and its EJBs are destroyed as well. You can try this by replacing the various annotations (@Stateless and @Singleton) by @Stateful[4]. Nevertheless, we don’t like this approach since it is a misusage of the concept and too artificial since it would not be used in a real-world application.
    1. The decision to use a SSB or a SFSB should be only dependent on the requirement for storing state information during the work with the respective EJB. In our application, a method call on the EJB terminates the work with it immediatelly. A subsequent method call needs no information about previous calls to perform its work. Hence, the SSB is the correct choice!
    2. Our sample application simplifies the data management due to didactic reasons. In a real-world application, we must implement some kind of user administration and a database would be essential too.  It is quite obvious that memos of a certain user should survive sessions and should be even accessible if the server had to be restarted. Since we do not have user administration, the session has the role of a user login. The MemoStore Singleton acts as the global data base. Consequently, instead of using SFSBs, we should improve the data management by storing the memos for the corresponding session id in the MemoStore bean. We surrender to implement that because we’ll find a better solution in the next chapter.[5]

In the following section, we introduce a new component technology called Contexts and Dependency Injection (CDI) that comprises more functionality of JSF Managed Beans and EJB and seems to be the future of Java EE development.

6.3 Further Reading

At the end of the chapter some references to textbooks that give a more complete introduction to EJBs and might help to deepen your understanding.

Jonathan Wetherbee, 2013. Beginning EJB 3, Java EE, 7th Edition. 2nd Edition. Apress.

Debu Panda, 2014. EJB 3 in Action. Second Edition Edition. Manning Publications.

 

Discussion

Use the message board below to give the authors your feedback or to discuss this page’s topic with other readers (in English please!). Please don’t expect the authors to answer directly, but they might update the content of this site according to your feedback.


  1. Remember, if you sign up to Codenvy, you will be able to persist the project in your workspace. This enables you to store intermediate states of your project and, by that, to make a break when ever you like. Otherwise, all your changes are volatile and will be lost after closing the browser.
  2. The class uses our model class Memo introduced in the previous section. At this point it might happen that Codenvy can't resolve the type MemoServiceBean. In this case, please update the dependencies of the project (menu item Build → Update Dependencies) and rebuild the project manually (menu item Build → Build). After a few seconds the error marks will disappear.
  3. At this point, you might not be convinced of our choice. Anyway, please, follow us for the moment. We will discuss alternatives in section 6.2.3.
  4. In this case, both EJB classes must implement java.io.Serializable.
  5. In a real-world application the MemoStore bean would encapsulate the access to the data base and would realize the O/R mapping. Such a bean is called Data Access Object (DAO). For this reason it would be realized as a SSB too.