Читать книгу Beginning Spring - Höller Jürgen - Страница 10

1
POJO Programming Model, Lightweight Containers, and Inversion of Control
POJO PROGRAMMING MODEL

Оглавление

POJO means Plain Old Java Objects. The name was first coined by Martin Fowler, Rebecca Parsons, and Josh MacKenzie to give regular Java objects an exciting-sounding name. It represents a programming trend that aims to simplify the coding, testing, and deployment phases of Java applications – especially enterprise Java applications.

You'll have a better understanding of what problems the POJO programming model solves if you first understand what problems the old EJB programming model had.

Problems of the Old EJB Programming Model

The Enterprise JavaBeans (EJB) technology was first announced around 1997. It offered a distributed business component model combined with a runtime platform that provided all the necessary middleware services those EJB components needed for their execution. It was a main specification under the J2EE specification umbrella at the time.

Many people were really excited by the promise of the EJB technology and J2EE platform. EJBs were offering a component model that would let developers focus only on the business side of the system while ignoring the middleware requirements, such as wiring of components, transaction management, persistence operations, security, resource pooling, threading, distribution, remoting, and so on. Developers were told that services for middleware requirements could be easily added into the system whenever there was any need of them. Everything seemed good and very promising on paper, but things didn't go well in practice.

The EJB 2.x specification required that the component interface and business logic implementation class extend interfaces from the EJB framework package. These requirements created a tight coupling between the developer-written code and the interface classes from the EJB framework package. It also required the implementation of several unnecessary callback methods, such as ejbCreate, ejbPassivate, and ejbActivate, which are not directly related to the main design goal of EJB.

To develop an EJB component, developers had to write at least three different classes – one for home, one for remote interfaces, and one for business objects, as shown here:


The preceding code snippet shows the minimum amount of code that needs to be written in order to create an EJB component with only one method using the EJB2 application programming interface (API). Although the remote interface defined the public API of the business object class to the outside world, a non-mandatory requirement in the specification asked that the business object class implementation not depend on the remote interface directly. When developers obeyed this warning, however, they were opening up a possibility that business object class implementation and its public API remote interface would become unsynchronized whenever the method declarations were modified in one of those classes. The solution was to introduce a fourth interface, which was implemented by the business object class and extended by the remote interface to keep the remote interface and the business object class implementation synchronized while not violating this non-mandatory requirement.

There were actually two interfaces that defined the public API of the business object class: the remote and local interfaces. Local interfaces were introduced to the EJB specification when people realized that remote interfaces were causing unnecessary performance overheads in systems in which there were no physically separated layers, and there was no direct access to the EJB layer from another client in the architecture, except through servlets. However, when developers needed to make EJB components remotely available they had to create a remote interface for them. Although there was no direct dependency between the business object class and its remote interface, all public methods of the business object implementation class had to throw RemoteException, causing the business object implementation class to depend on EJB and remoting technologies.

Testability was one of the biggest problems of the old EJB programming model. It was almost impossible to test session and entity beans outside the EJB container; for example, inside an integrated development environment (IDE) using JUnit. This is because dependencies of those session beans were satisfied through local or remote interfaces, and it was very hard – but not impossible – to test session beans in a standalone environment. When it came time to run or test entity beans outside the container, things were more difficult because the entity bean classes had to be abstract and their concrete implementations were provided by the EJB container at deployment time. Because of such difficulties, people tried to access the EJBs deployed in the container and test them using in-container test frameworks, such as Cactus. Nevertheless, such solutions were far from the simplicity and speed of running tests within a standalone environment by right-clicking and selecting Run As JUnit Test.

The deployment process was another time-consuming and error-prone phase of the EJB programming model. Developers used deployment descriptor files in XML format to deploy developed EJB components, but configuring their middleware requirements, such as transaction semantics, security requirements, and so on, caused those files to become several hundred lines long. Developers usually were trying to maintain the files by hand, and it was quite easy to make simple typos in package or class names, and those errors wouldn't be noticed until deployment time.

The following code snippet contains two EJB definitions, one depending on the other, and it includes a container-managed transaction configuration as well. Imagine how things can go wrong when you have dozens of other EJB definitions, each having its own dependencies, transaction management, security configurations, and so on:


One very common task while coding EJBs was to access the Java Naming and Directory Interface (JNDI) context in the J2EE environment and perform object lookups so that necessary dependencies to other EJBs and DataSource instances could be satisfied. However, this was causing the EJB component to become tightly coupled with the container, and unit testing was hard to perform because of this environmental dependency. The following code snippets show how an EJB home object and javax.sql.DataSource are looked up from a JNDI repository:


Actually, JNDI lookup can be considered an early form of dependency injection, but, due to its pull-based nature, it was difficult to isolate components during unit testing because of the dependency to the JNDI context.

Another problem of the old EJB programming model was that it diverted developers toward the procedural programming style. Application behavior in this style of programming is mainly handled within some methods, while data from and to those methods is carried with dumb domain model objects. Unfortunately, data and behavior are separated from each other and are not in a cohesive form in such a case. This is definitely a divergence from the object-oriented programming perspective in which one of the important characteristics is encapsulation of data together with the related behavior. After all, you are using an object-oriented programming language called Java, and you want to take advantage of all its abilities, don't you?

The main reason for such a paradigm shift, while using an object-oriented language, was the EJB programming model. People usually were developing session- and message-driven beans that were stateless, monolithic, and heavyweight components in which all the business logic was implemented with data access operations inside them. Entity EJBs were expected to represent the domain model, but they had some subtle deficiencies that prevented them from being used at all. For example, inheritance support was too limited, and recursive calls within entity beans were not supported; it was not possible to transfer the entity bean instances as session and message-driven bean method inputs and return values, and so on.

People might think that procedural style is not a big problem for scenarios in which business logic is simple. However, things don't stay simple in real-life enterprise application projects. As new requirements come along, things become more complex and written code grows to be more and more of a maintenance headache. The procedural style of programming that was promoted by the old EJB programming model caused the creation and use of dumb domain objects, which were acting purely as data transfer objects between the application layers and the network. Martin Fowler coined the term anemic domain model for such problematic domain objects. Anemic blood is missing vital ingredients; similarly, an anemic domain model is also limited to only data transfer and persistence-related operations, and it contains hardly any behavioral code. Unfortunately, the old EJB programming model was not able to enforce operating on a fine-grained and rich object model behind a coarse-grained component model.

Enterprise applications usually have layered architectures. They are mainly composed of the web, service, and data access layers. Figure 1.1 shows those logical layers and the relationships between each.


FIGURE 1.1


Each layer should only know and interact with the layer just beneath it. That way, upper layers aren't affected by changes made within other layers upon which they don't directly depend. It also becomes possible to easily replace layers because only one layer depends on another, and only that dependent layer will have to be changed if there is a need.

It is a desirable and correct approach to divide the system into several logical layers. However, this doesn't mean that there should always be a one-to-one correspondence between physical layers. Unfortunately, having an EJB container caused those web and service layers to work using remote method invocation (RMI), which is practically equivalent to having separate physical layers. Hence, servlet and JavaServer Pages (JSP) components in the web layer have complex and performance-degrading interactions with the EJB components in the service layers. Apart from inefficient network interaction, developers also experienced class- and resource-loading issues. The reason for these issues were that the EJB container used a different ClassLoader instance than the web container.

Figure 1.2 shows a typical physical layering of a J2EE application. The application server has separate web and EJB containers. Therefore, although they are located in the same server instance, web components have to interact with EJB components as if they are in different physical servers using RMI. It is observed in many enterprise Java applications that RMI calls from the web to the service layers create an unnecessary performance cost over time when the web and EJB layers are located in the same physical machine, and the EJB layer is only accessed from the web layers. As a result, local interfaces were introduced to get rid of RMI between those layers.


FIGURE 1.2


The “write once and run everywhere” slogan was very popular at those times, and people expected it to be true among J2EE environments as well. However, there were lots of missing and open issues in EJB and J2EE specifications, so many enterprise projects had to develop solutions specific to their application servers. Every application server had its own legacy set of features, and you had to perform server-specific configurations, or code against a server-specific API to make your application run in the target environment. Actually, the slogan had turned into “write once and debug everywhere,” and this was a common joke among J2EE developers.

Most of the aforementioned problems were addressed in the EJB 3 and EJB 3.1 specifications. The most important point during those improvements is that the POJO programming model was taken as a reference by those newer EJB specifications. Session and message-driven beans are still available but much simpler now, and entity beans are transformed into POJO-based domain objects with the Java Persistence API (JPA). It is now much easier to implement, test, and deploy them. The EJB programming model has become more and like the POJO programming model over time.

Certainly, the biggest contribution to improve the EJB component model and J2EE environment has come from POJO-based, lightweight frameworks, such as Hibernate and Spring. We can safely say that the EJB programming model mostly was inspired by those frameworks, especially Spring.

Benefits of the POJO Programming Model

The most important advantage of the POJO programming model is that coding application classes is very fast and simple. This is because classes don't need to depend on any particular API, implement any special interface, or extend from a particular framework class. You do not have to create any special callback methods until you really need them.

Because the POJO-based classes don't depend on any particular API or framework code, they can easily be transferred over the network and used between layers. Therefore, you don't need to create separate data transfer object classes in order to carry data over the network.

You don't need to deploy your classes into any container or wait for long deployment cycles so that you can run and test them. You can easily test your classes within your favorite IDE using JUnit. You don't need to employ in-container testing frameworks like Cactus to perform integration unit tests.

The POJO programming model lets you code with an object-oriented perspective instead of a procedural style. It becomes possible to reflect the problem domain exactly to the solution domain. Business logic can be handled over a more fine-grained model, which is also richer in terms of behavioral aspects.

Beginning Spring

Подняться наверх