Leverage new Java EE 6 features to build simple and maintainable applications.
The introduction of Java Platform, Enterprise Edition (Java EE) 5, in 2006, did a lot to simplify enterprise application development. Java EE 6, released in 2009, simplifies design and architecture tasks even further. Java EE 6 is a good choice for building small situational applications quickly and without any overhead. This article discusses various Java EE 6 architectures and design approaches that help developers build efficient, simple, and maintainable apps.
Java EE 6 consists of a set of independent APIs released together under the Java EE name. Although these APIs are independent, they fit together surprisingly well. For a given application, you could use only JavaServer Faces (JSF) 2.0, you could use Enterprise JavaBeans (EJB) 3.1 for transactional services, or you could use Contexts and Dependency Injection (CDI) with Java Persistence API (JPA) 2.0 and the Bean Validation model to implement transactions.
With a pragmatic mix of available Java EE 6 APIs, you can entirely eliminate the need to implement infrastructure services such as transactions, threading, throttling, or monitoring in your application. The real challenge is in selecting the right subset of APIs that minimizes overhead and complexity while making sure you don’t have to reinvent the wheel with custom code. As a general rule, you should strive to use existing Java SE and Java EE services before expanding your search to find alternatives.
CDI: The Standard Glue
CDI, introduced with Java EE 6 to act as a glue for the different parts of the Java EE 6 specification, manages the lifecycle of POJO (Plain Old Java Object) beans and uses a type-safe mechanism for dependency injection. CDI also introduces many powerful features such as events, interceptors, decorators, standardized extension points, and the service provider interface.
Because CDI is new and designed to be an integration layer, there is some overlap with older technologies. Although you can continue to use EJB 3.1 injection or JSF managed beans directly, you should consider using CDI wherever possible. CDI is more powerful, and you can simplify your application by using a single API.
CDI uses annotations to perform dependency injection. The most important annotation is javax.inject.Inject. The example in Listing 1 shows how this annotation can be used to inject a POJO into a servlet. All you need to do is to declare a field and annotate it with @Inject. When that code is executed, the container automatically initializes fields annotated with the @Inject annotation before the execution of any business methods.
Code Listing 1: POJO injection into a servlet with @Inject
@WebServlet(name="HelloWorldService", urlPatterns={"/HelloWorldService"}) public class HelloWorldHTTPService extends HttpServlet { @Inject private Hello hello; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println(hello.helloWorld()); out.flush(); out.close(); } }
There are no specific requirements for the injected class, beyond having to contain a default constructor:
public class Hello { public String helloWorld(){ return "Hello World"; } }
To make the above example work, you would also need an empty beans.xml deployment descriptor with the following content: <beans></beans>. The existence of this configuration file in the WEB-INF folder activates CDI capabilities.
Note that the Hello class is a POJO and not an EJB. It doesn’t have to be declared or configured—the @Inject annotation ensures proper creation and lifecycle management. In the real world, you would rarely inject POJOs into a servlet; you would probably use a UI framework (such as JSF 2) or expose your service via representational state transfer (REST). In such cases, the use of CDI is even more beneficial.
To illustrate, consider a simple MessageMe application that stores a message string in a database. The JSF 2 markup consists of two components: inputText and commandButton. As shown in Listing 2, inputText is value-bound to a class with the name index with a property message, which has, in turn, a content attribute. commandButtons’s action attribute is bound to the save method of the backing bean with the name index.
Code Listing 2: index.xhtml: binding the values to a CDI backing bean
<h:body> <h:form> Content:<h:inputText value="#{index.message.content}"/> <br/> <h:commandButton value="Save" action="#{index.save}"/> </h:form> </h:body>
Listing 3 shows the backing bean implemented as a request-scoped CDI bean, using the @RequestScoped annotation for request handling. A JSF 2 managed bean (using the @ManagedBean annotation) could also work, but CDI is just as powerful. And using CDI everywhere simplifies the architecture, with a single glue API across all application layers.
Code Listing 3: A CDI backing bean with injected EJB
package com.tm.messageme.presentation; import com.tm.messageme.business.messaging.boundary.Messaging; import com.tm.messageme.business.messaging.entity.Message; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; @Named @RequestScoped public class Index { @Inject Messaging ms; private Message message = new Message(); public Message getMessage() { return message; } public void save(){ ms.store(message); } }
The annotation @Named (as specified in the JSR 330 specification and implemented in Guice and Spring) makes the index backing bean visible in all expression language (EL) markup. It works according to the “convention over configuration” principle: the name of the backing bean in JSF 2 is derived from the class name. The first letter is not capitalized.
The Message class is implemented as a JPA 2 entity, as shown in Listing 4.
Code Listing 4: JPA 2 entity validated with Bean Validation
package com.tm.messageme.business.messaging.entity; @Entity public class Message { @Id @GeneratedValue private Long id; @Size(min=2,max=140) private String content; public Message(String content) { this.content = content; } public Message() { /*required by JPA */} public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Long getId() { return id; } }
The next class in this example is the Messaging class, which is implemented as an EJB 3.1 session bean. This class represents a pragmatic exception to the “CDI everywhere” rule. EJBs provide many capabilities, such as transactions, pooling, Java Management Extensions (JMX) monitoring, and asynchronous execution—all for the price of a single additional @Stateless annotation. In future Java EE releases, these aspects are likely to be extracted from EJBs and made available in CDI as well. In Java EE 6, however, a boundary or facade of a business component is most effectively implemented as a stateless session bean.
The @Asynchronous annotation in Listing 5 is particularly interesting. It enables the asynchronous but transactional execution of methods and is available only for EJBs. Note that the Messaging EJB is injected with @Inject and not @EJB. In practice, either annotation would work, with virtually no difference. The use of @Inject is slightly more powerful and supports inheritance. The @EJB annotation, on the other hand, works only with EJBs.
Code Listing 5: A boundary implemented as an EJB session bean
package com.tm.messageme.business.messaging.boundary; import com.tm.messageme.business.messaging.control.MessageStore; import com.tm.messageme.business.messaging.entity.Message; import javax.ejb.Asynchronous; import javax.ejb.Stateless; import javax.inject.Inject; @Stateless public class Messaging { @Inject MessageStore messageStore; @Asynchronous public void store(Message message){ messageStore.store(message); } }
The MessageStore class in Listing 6 is a Data Access Object (DAO) that encapsulates access to the EntityManager.
Code Listing 6: A CDI bean from the control layer
package com.tm.messageme.business.messaging.control; import com.tm.messageme.business.messaging.entity.Message; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; public class MessageStore { @PersistenceContext EntityManager em; public void store(Message message){ em.persist(message); } }
If you review the packaging of the application described above, you will notice separate boundary, control, and entity packages. This packaging approach is an implementation of the Entity Control Boundary (ECB) pattern. The boundary layer is the facade, the control layer is responsible for the implementation of process- and entity-independent logic, and the entity layer contains rich domain objects.
With Java EE 6 and especially the availability of JPA 2, CDI, and EJB, the implementation of all three layers can lead to empty delegate code. For example, many CRUD-based use cases can be implemented very efficiently with a single boundary acting as a facade for accessing multiple entities.
However, a direct one-to-one relationship between the concepts in the ECB pattern and packages inside a component can still be beneficial. When packages are kept separate, static analysis tools can be used more easily to measure dependencies between packages. Furthermore, frameworks such as OSGi and Jigsaw rely on the existence of separate packages to expose public APIs.
In Java EE 6, the boundary is always realized with EJBs. The control layer can contain either CDIs or EJBs, and the entity layer can contain either JPA 2 entities or transient, unmanaged entities. The final decision of whether to use a CDI or an EJB in the control layer does not have to be made up front. You can start with a CDI and convert it into an EJB down the road by using the @Stateless annotation. You may need to use an EJB in some cases, such as when you need to start a subsequent transaction with @RequiresNew, when you need to execute a method asynchronously, or when you need to roll back the current transaction by invoking SessionContext.setRollbackOnly().
CDI, on the other hand, is more suitable for integrating legacy code or implementing Strategy, Factory, or Observer software design patterns. All of these capabilities are already built in and result in far less code than with the Java SE counterpart.
When you are developing applications with the ECB pattern, the ECB layering should evolve iteratively and not be forced in a top-down way. You should start with the persistence (Entity) layer, perform unit testing, and then implement the boundary layer. For building the unit test, the EntityManager and the associated transactions need to be created and managed manually (as shown in Listing 7).
Code Listing 7: Standalone JPA unit tests
package com.tm.messageme.business.messaging.entity; import javax.persistence.*; import org.junit.Test; @Test public void mappingSmokeTest() { EntityManagerFactory emf = Persistence.createEntityManagerFactory("test"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); em.persist(new Message("duke")); tx.commit(); }
The persistence.xml file must also be adjusted to handle standalone execution. Specifically, the transaction type should be changed to RESOURCE_LOCAL and a JDBC connection (instead of a datasource) must be configured explicitly, as shown in Listing 8.
Code Listing 8: persistence.xml for standalone JPA unit tests
<persistence> <persistence-unit name="test" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <class>com.abien.messageme.business.messaging.entity.Message</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:derby: ./sample;create=true"/> <property name="javax.persistence.jdbc.password" value="app"/> <property name="javax.persistence.jdbc.driver" value="org.apache.derby .jdbc.EmbeddedDriver"/> <property name="javax.persistence.jdbc.user" value="app"/> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> </properties> </persistence-unit> </Persistence>
When building the control layer, note that its content will be the product of entity and boundary layer refactoring. The reusable and noncohesive parts of the boundary layer, such as queries, algorithms, or validations, along with cross-cutting concerns from the entity layer, will be extracted into CDI managed beans in the control layer.
Using the CEC Pattern
The main purpose of the boundary in the ECB pattern is to provide a clear separation between business and presentation logic. By definition, the boundary needs to be independent of presentation logic. Even with the many compromises you may make in your architecture, a clear separation between the business and UI technology is a must. In practice, UI logic tends to vary more frequently than business logic. It is common to produce business logic that can be accessed by a Web client (such as JSF 2), a rich client (such as Swing or Eclipse RCP), and REST at the same time.
In the case of JSF 2, CDI is again the easiest choice for implementing a controller or a presenter. CDI managed beans can be directly bound to the JSF 2 view via EL, and the boundary (EJB 3.1) can be directly injected into the presenter. The presenter (or a controller) can be directly captured with an @Stereotype annotation. It works like a macro—you can place in it CDI annotations that get expanded with the annotation. A stereotype is a regular Java annotation represented by @Stereotype:
@Named @RequestScoped @Stereotype @Retention(RUNTIME) @Target(TYPE) public @interface Presenter {}
This custom stereotype can be applied instead of @Named and @RequestScoped—just like a macro. All CDI annotations identifying the presenter pattern can then be replaced with
@Presenter public class Index {//}
The purpose of the presenter is to implement presentation logic. The structure of the presenter is tightly coupled with the view, in that the state of a JSF component in the view is mapped to a property inside the presenter. The property can be either a value (with value binding) or the component instance itself (with component binding). In trivial cases, there is a one-to-one relationship between the view and the presenter. The presenter contains the view’s data as well as all the presentation logic. Injecting the boundary into the presenter involves using the @Inject annotation.
As the amount of presentation logic grows inside the presenter, the code can become harder to maintain and test. With CDI, it is fairly easy to split the monolithic presenter into separate data and presentation logic parts. For example, the following code shows how to refactor the backing bean from the earlier example by moving the save method into a newly created IndexPresenter bean. The presenter annotation is duplicated and renamed @View, and the bean is renamed IndexView:
@View public class IndexView { private Message message = new Message(); public Message getMessage() { return message; } }
The IndexPresenter bean gets the old @Presenter annotation. As the following code shows, the only purpose of the IndexPresenter bean in this case is to implement the presentation logic.
@Presenter public class IndexPresenter { @Inject Messaging boundary; @Inject IndexView indexView;
public void save(){ boundary.store(indexView.getMessage()); } }
Because the boundary and the view are injected into the IndexPresenter, they can be easily mocked out. In a unit test environment, both fields would be set directly with the mock, whereas in a production environment, the container would perform the injection and set the actual dependency. Because the unit test and the IndexPresenter reside in the same package, the default visible fields can be set directly. Private fields with public setters can be used, but packagewide fields are good enough in most cases and can reduce code size.
Listing 9 shows how to test the presentation logic by mocking out the IndexView as well as the boundary Messaging class. The test, which invokes the IndexPresenter.save() method, is successful if the method store gets invoked exactly once with the Message-Instance being returned by the IndexView. Verifying the invocation means passing the mock to the Mockito.verify() method. The IndexView is mocked out to manipulate the return value without interacting with the JSF rendering.
Code Listing 9: IndexPresenterTest—with mocked-out view and boundary
package com.tm.messageme.presentation; //...other imports import org.junit.Before; import org.junit.Test; import static org.mockito.Mockito.*; public class IndexPresenterTest { private IndexPresenter cut; @Before public void initialize(){ this.cut = new IndexPresenter(); } @Test public void save() { this.cut.boundary = mock(Messaging.class); this.cut.indexView = mock(IndexView.class); Message expected = new Message("duke"); when(this.cut.indexView.getMessage()).thenReturn(expected); cut.save(); verify(this.cut.boundary,times(1)).store(expected); } }
The Messaging boundary is mocked out for a different reason: to verify that the expected method actually gets invoked:
public void save(){ boundary.store(indexView.getMessage()); }
The design of the JSF 2 presentation is similar to that of a rich Swing application. Common patterns such as Model-View-Controller and their refinements—Supervising Controller and Passive View—can be applied to JSF 2 as well. The main difference between JSF and a rich client technology is the way the view is rendered. In Swing the developer implements the view in Java, whereas in JSF 2 the developer uses XHTML markup. In JSF 2 the values of the component can be directly bound to a corresponding class, whereas in Swing they are usually stored in the view itself or in the model.
For the implementation of data-driven use cases such as CRUD, the Supervising Controller is a better choice than the Passive View. In the Supervising Controller pattern, a single backing bean (IndexView) is responsible for managing both the presentation logic and the state of the view. In more-sophisticated use cases, the Passive View variant may be more applicable. In the Passive View pattern, the backing bean is split into the view and presentation logic, and the presentation logic is extracted from the IndexView to the IndexPresenter.
CDI is best suited for the implementation of the presentation layer. Because of the built-in cross-cutting concerns (such as transactions, concurrency, asynchronous execution, monitoring, and throttling), the boundary of the business logic is realized as an EJB. The business component can be realized either as EJBs or CDIs. In general, you can start with CDIs and, over time, replace managed beans in special cases with EJBs. The CDI-EJB-CDI (CEC) pattern is the simplest and most pragmatic choice for Java EE 6.
Making Interfaces Useful
EJB 3.0 (in Java EE 5) required separate interfaces for bean classes. To avoid naming collisions, developers often had to resort to well-defined naming conventions such as XyzLocal/XyzRemote and XyzBean. In Java EE 6, interfaces for EJBs and CDIs are now optional. Public EJB or CDI methods can now expose a “no interface” view, with no loss of functionality.
This new functionality makes interfaces meaningful again. As opposed to the obligatory, nondescript use of interfaces with earlier releases, interfaces in Java EE 6 can be used for the implementation of the Strategy pattern; implementation of a public API; or strict separation of modules, which makes the code more expressive. An interface can also signal the “protected variations” of a system, and direct dependencies between classes can be used for code that is less likely to vary.
You can safely start without any interfaces and introduce them later as the need arises. This approach is fundamentally different from that in Java EE 5. Compared to Java 2 Platform, Enterprise Edition (J2EE) from 2003, Java EE 6 code is simpler, in terms of the elimination of several layers, indirections, and abstractions. Unlike J2EE, Java EE 6 consists of annotated classes without any dependencies on the platform. This approach eliminates the need to separate business logic from the infrastructure and makes the majority of J2EE patterns and best practices superfluous. In Java EE 6, simple cases can be solved with two layers: presentation and business logic. The EntityManager is already a good enough abstraction of the underlying persistence, so there is no need for additional indirections.
Maintainable Java EE 6 applications are written according to the YAGNI (You Ain’t Gonna Need It), DRY (Don’t Repeat Yourself), and KISS (Keep It Simple, Stupid) principles. Design patterns and best practices are introduced in a bottom-up—not a top-down—fashion. The patterns are always motivated by functional and nonfunctional requirements, not by the shortcomings of the platform. This approach represents the biggest difference between Java EE 6 and previous J2EE releases. In J2EE many of the design decisions were made up front, driven by the J2EE platform dependencies.
By contrast, the Java EE 6 development process focuses on function:
- Write simple code that directly solves the business problem.
- Verify the business logic with unit tests.
- Cut redundancies and improve the design with refactoring.
- Stress-test your application.
- Go back to 1.
Design and architecture are driven by concrete requirements rather than generic and architectural best practices. By continually stress-testing your application (at least once per week), you can more easily justify a simple design with hard facts to gain insight into system behavior under stress.
No comments :
Post a Comment