Interfaces on Demand with CDI and EJB 3.1
by Adam Bien
Published January 2012
Interfaces are no longer required with Java EE 6, so you can use them more consciously for the realization of business logic.
Introduction
Since Java Platform, Enterprise Edition 6 (Java EE 6), interfaces are no longer required by the container in order to realize common use cases. Transactions, security, custom aspects, concurrency, and monitoring are also available for plain classes without any interfaces. Java EE 6 made interfaces meaningful again. Since they are no longer required by the platform, you can use them more consciously for the realization of business logic. An interface becomes a vehicle for encapsulation or abstraction, as it was originally intended to be:
“Interfaces are used to encode similarities which the classes of various types share, but do not necessarily constitute a class relationship. For instance, a human and a parrot can both whistle; however, it would not make sense to represent Humans and Parrots as subclasses of a Whistler class. Rather they would most likely be subclasses of an Animal class (likely with intermediate classes), but both would implement the Whistler interface.” [http://en.wikipedia.org/wiki/Java_interface]
Unfortunately, interfaces are not used within the context of Java EE as a tool for encoding similarities; they serve as reassurance for possible, but unlikely, future enhancements. Extensive use of interfaces derives from a belief that they might be helpful in the future.
Premature Extensibility Is the Root of Some Evil
In theory, you could make everything extensible with interfaces. If you used interfaces for everything, you could swap the implementations without recompiling the clients. However, such extensibility comes with a price. The number of artifacts will double and you will have to introduce a configuration facility.
The purpose of a configuration is the management of associations between interfaces and their implementations. At least the unique name of the interface and the name of the interface’s realization have to be maintained in the configuration, and that leads to duplication. Exactly the same information is stored in the interfaces as well as in classes. Every change to the fully qualified name of the class or interface has to be performed atomically. The source files and the configuration have to be changed at the same time.
When You Cannot Find a Name
When the only motivation for the introduction of an interface is not the abstraction of an already existing implementation, but rather for a future extension, you will run into naming clashes between the interface and its implementation. You will not be able to name the interface and its implementation after their responsibilities. A common workaround to this problem is the addition of an "I" prefix to the interface or an Impl suffix to the implementation. For example, you will see code such as Communicator communicator = new CommunicatorImpl();.
Such naming does not help you to recognize the responsibilities at all. Rather, it emphasizes already known facts: A class is, of course, an implementation of an interface declared after the implement's keyword.
It gets even worse, though. Although the impetus behind such a naming scheme is the introduction of future extension points, it is impossible to introduce another implementation with reasonable naming. (CommunicatorImpl2 is not a reasonable name.) So if you cannot name the implementation or interface properly, do not introduce an interface.
Back to the Natural Contract
The contract of a class comprises all of its public methods. The public methods are intended to be used by their clients. The no-interface view of an Enterprise JavaBeans 3.1 bean is defined exactly as follows in Chapter 3.4.4 of the EJB 3.1 specification (JSR 318):
“…A Session Bean’s no-interface view is a variation of the Local view that exposes the public methods of the bean class without the use of a separate business interface…”
All private methods are hidden. Methods with package-private and protected visibility are visible only to classes in the same package and they are usually accessed only for test purposes. A JUnit test class resides in the same package as the “Class Under Test” (CUT) and mocks-out the inconvenient references, usually accessing the package-private or protected fields directly.
As Simple as Possible…
There is also no interface required to expose an existing EJB 3.1 bean via Representational State Transfer (REST) (see Listing 1). A MessageResource is an EJB 3.1 bean directly exposed as Java API for RESTful Web Services (JAX-RS) endpoint.
@Path("messages")
@Stateless
@Produces(MediaType.APPLICATION_JSON)
public class MessagesResource {
@Inject
Communicator communicator;
@GET
public List<Message> allMessages(){
return communicator.getRecentMessages();
}
}
Listing 1: A Sample REST Endpoint
You could annotate interfaces with JAX-RS and hide some JAX-RS annotations this way. However, such a separation is only nice in theory. In practice, a more complex application will need access to JAX-RS specific classes, such as javax.ws.rs.core.UriInfo, javax.ws.rs.core.UriBuilder, or javax.ws.rs.core.Response, and it will be dependent on the JAX-RS API anyway. In fact, in all non-trivial applications, it is a good idea to split the protocol-dependent part (SOAP, JAX-RS, CORBA, and so on) and the pure boundary part into two independent classes.
Although the class Communicator is injected directly, it can be still intercepted. Java EE 6 interceptors (see Listing 2) work equally well with interfaces and with plain Java classes.
public class CommunicationSniffer {
@AroundInvoke
public Object sniff(InvocationContext ic) throws Exception{
Object result = ic.proceed();
System.out.printf("Method %s returned %s",ic.getMethod(),result);
return result;
}
}
Listing 2: Interface-less Method Call Interception with CommunicationSniffer
You only have to declare an interceptor in an annotation or XML descriptor to apply cross-cutting concerns to your class. You could also implement an interface and inject it, but you don’t have to do so.
@Interceptors(CommunicationSniffer.class)
public class Communicator {
public List<Message> getRecentMessages(){
return new ArrayList<Message>(){{
add(new Message("first"));
add(new Message("second"));
}};
}
}
Listing 3: An Intercepted POJO
A GET request of the URI [WAR_NAME]/resources/messages is processed by the method allMessages, which delegates to the injected Communicator#getRecentMessages() instance. The call is intercepted by the CommunicationSniffer interceptor, which writes the following string to the standard-out stream:
Method public java.util.List
com.abien.patterns.nointerfaces.control.Communicator.getRecentMessages()
returned [Message{content=first}, Message{content=second}].
No interface is required for the serialization of the Java Architecture for XML Binding (JAXB)-POJO Message either (see Listing 4). Interface introduction for the Message entity would be, in fact, rather confusing in this case. There are no methods to be specified in a contract.
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Message {
private String content;
public Message(String content) {
this.content = content;
}
public Message() {}
}
Listing 4: An Entity Message with JAXB Annotations
…But Not Simpler
Now the MessageResource is directly dependent on the implementation of the class Communicator. With a direct dependency on the implementation, the Communicator cannot be easily swapped out anymore. You will have to change the MessageResource code to replace Communicator with other implementations.
Let’s introduce another variation of the Communicator class called ConfidentialCommunicator. It returns a different message and is not intercepted by the CommunicationSniffer interceptor (see Listing 5).
public class ConfidentialCommunicator {
public List<Message> getRecentMessages(){
return new ArrayList<Message>(){{
add(new Message("top secret"));
}};
}
}
Listing 5: An Alternative Communicator
We could inject both the ConfidentialCommunicator and the Communicator at the same time and decide inside the MessageResource which instance to use. Such a solution would be unnecessarily complex, hard to understand, and so also hard to maintain.
The first Communicator version was intercepted by the CommunicationSniffer and didn’t care a lot about privacy. So let’s rename it to PublicCommunicator. Hence, we have a ConfidentialCommunicator and a PublicCommunicator with different responsibilities, but identically signaturing them is a natural next step to introducing an interface to hide the implementation specifics and define a common contract.
The MessageResource does not care about the specific implementations. It only wants to serialize a List<Message> into a JavaScript Object Notation (JSON) string. The name of the class happens to be a good name for an interface as well: just Communicator.
Interestingly, the MessageResource class remains unchanged after the interface introduction:
public class MessagesResource {
@Inject Communicator communicator;
//…
}
Actually, MessagesResources shouldn’t care about the implementation specifics. It is interested only in a single method: getRecentMessages. It could be implemented by a single public class or by an interface that hides different implementations. Both cases look exactly the same from the MessageResources perspective.
Flexibility Comes with a Price
Now we have a Communicator interface directly injected into the MessageResource EJB 3.1 bean. The existence of an interface implies multiple implementations: ConfidentialCommunicator and PublicCommunicator.
Now we have to choose between these implementations. The direct injection of the Communicator interface does not work anymore without configuration, and it causes the following error:
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies
for type [Communicator] with qualifiers [@Default] at injection point
[[field] @Inject com.abien.nointerfaces.boundary.MessagesResource.communicator].
Possible dependencies [[Managed Bean [class
com.abien.nointerfaces.control.PublicCommunicator] with qualifiers
[@Any @Default], Managed Bean [class
com.abien.nointerfaces.control.ConfidentialCommunicator] with qualifiers
[@Any @Default]]].
We can disambiguate the dependency by deactivating one of the managed beans. Denoting a managed bean with the javax.enterprise.inject.Alternative annotation deactivates it:
@Alternative
public class PublicCommunicator implements Communicator{}
Now the relation is one-to-one. The Communicator interface can be directly injected into the MessagesResource. Invocation of the URL generates the following output, which is generated by the ConfidentialCommunicator implementation:
{"message":{"content":"top secret”}}
You have to move the annotation from the PublicCommunicator to the ConfidentialCommunicator to re-activate the PublicCommunicator, and you have to recompile the whole application to switch between implementations.
You could also deactivate both and activate the implementation of your choice in beans.xml (Listing 6).
<beans>
<alternatives>
<class>com.abien.nointerfaces.control.ConfidentialCommunicator</class>
</alternatives>
</beans>
Listing 6: Alternative Activation in beans.xml with Fully Qualified Managed Beans
Now the fully qualified name of the class has to be maintained in the XML file as well as in the source code. Hierarchy changes or even simple renaming have to be performed in two places at once. Contexts and Dependency Injection (CDI) stereotypes provide an elegant solution to the data replication problem. Instead of repeating the fully qualified class names only a tag interface is specified (see Listing 7).
<beans>
<alternatives>
<stereotype>com.abien.nointerfaces.control.Confidential</stereotype>
</alternatives>
</beans>
Listing 7: Activation of Classes Annotated with a Stereotype
A stereotype is an annotation denoted itself with the @Stereotype meta-annotation (see Listing 8).
@Alternative
@Stereotype
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Confidential {}
Listing 8: An @Alternative @Stereotype
Now you can deactivate managed beans just by using the stereotype instead of the raw @Alternative annotation (see Listing 9).
@Confidential
public class ConfidentialCommunicator implements Communicator {}
Listing 9: Deactivation with a @Stereotype
All classes annotated with the @Confidential stereotype can be activated at once by specifying the fully qualified name of the stereotype instead of configuring the fully qualified class name of the managed bean. Moving a stereotype to another package or renaming it results in the same hassle: the Java source and the contents of the beans.xml file have to be maintained at the same time. Stereotype refactorings, however, are far less likely.
You can also annotate several classes with a single stereotype and activate them at once. The 1:n relation between a stereotype and managed beans makes a stereotype-based solution more maintainable. You don’t have to maintain all the fully qualified names of the managed beans in beans.xml.
Let the User Decide
Both the provider and user are able to configure the injection. Instead of aimed activation and deactivation of managed beans with beans.xml, annotations, or both, we can decide at the injection point which implementation to choose.
The ambiguity has to be resolved with a custom qualifier annotation for this purpose. The @Qualifier annotation is similar to the @Stereotype annotation, but it is used to resolve an ambiguous dependency by marking both parts with the same annotation with the same elements.
@Stereotype is a mix of a tag interface and a macro. You can annotate a stereotype with several annotations and make a class declaration to be more concise. All the used annotations in the stereotype annotation are expanded on the class level. In the previous example, we used the @Alternative annotation as a meta-annotation.
Instead of the @Confidential stereotype, we mark each implementation, as well as the injection point, with the @Confidentiality qualifier (see Listing 10).
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Confidentiality {
Level value();
enum Level{
STRONG, WEAK
}
}
Listing 10: A Custom Qualifier for Dependency Resolution
The Confidentiality qualifier comes with an embedded enumeration, Level, as well. The value of the Level enumeration is also used as matching criteria. Because no default value is specified, you will have to choose the value by applying the annotation.
The ConfidentialCommunicator is annotated with the STRONG Level value:
@Confidentiality(Confidentiality.Level.STRONG)
public class ConfidentialCommunicator implements Communicator {}
And the PublicCommunicator is annotated with the WEAK Level counterpart:
@Confidentiality(Confidentiality.Level.WEAK)
public class PublicCommunicator implements Communicator{}
The Communicator user can now decide which implementation to use by setting the value of the Level enumerator (see Listing 11).
public class MessagesResource {
@Inject @Confidentiality(Confidentiality.Level.WEAK)
Communicator communicator;
//..
}
Listing 11: A Qualifier at the Injection Point
The user is decoupled from a particular implementation and decides by specifying a qualifier at the injection point regarding which implementation to use. In contrast, with stereotypes, qualifiers are the most intrusive approach. The user’s code has to be extended with the qualifier annotation and re-compiled for every change. On the other hand, custom qualifiers are easy to maintain, because the compiler prevents any misspellings.
The Runtime Choice
You can even explore all implementations of the interface and decide which implementation to use at runtime. An injected javax.enterprise.inject.Instance provides the most flexibility. You can query the injected Instance to determine whether the dependencies are ambiguous or unsatisfied and iterate over all implementations.
With the @Any qualifier, all implementations of the Communicator interface are discovered regardless of whether they are annotated with a custom qualifier (see Listing 12).
@Path("messages")
@Stateless
@Produces(MediaType.APPLICATION_JSON)
public class MessagesResource {
@Inject @Any
Instance<Communicator> communicatorInstances;
@GET
public List<Message> allMessages(){
System.out.println("--isAmbiguous: " + communicatorInstances.isAmbiguous());
System.out.println("--isUnsatisfied: " + communicatorInstances.isUnsatisfied());
List<Message> allMessages = new ArrayList<Message>();
for (Communicator communicator : communicatorInstances) {
allMessages.addAll(communicator.getRecentMessages());
}
return allMessages;
}
}
Listing 12: Iteration Over All Realizations at Runtime
This is the JSON string generated by the Listing 12:
{"message":[{"content":"first"},{"content":"second"},{"content":"top secret”}]}
Interfaces on Demand
Java EE 6 does not require you to use interfaces for the implementation of JAX-RS, EJB, CDI, or Java Persistence API (JPA) components. You can start with direct injection of the class. There are no sacrifices to be made. Interceptors, transactions, security, monitoring, threading, and dependency injection all work equally well without interfaces.
If you name the implementation after its business responsibilities, the user of the injected class will have a hard time telling whether it is a concrete class, an abstract class, or an interface. Naming conventions such as Impl suffixes are counter-productive for that reason. Such naming conventions are also absolutely superfluous, because they emphasize obvious facts.
Sometimes, you will have to introduce another class and abstract both implementations with a formal contract--an explicit Java interface. This is only a minor refactoring. You will have to do the following:
- Rename the original implementation after its responsibilities. (We renamed Communicator to PublicCommunicator in the earlier example.)
- Introduce an interface with the name of the original implementation (Communicator). The original class has to implement this interface.
- Name the other class after its business responsibilities and implement the interface.
If you need more flexibility, you can introduce stereotypes or qualifiers or query the framework at runtime (with javax.enterprise.inject.Instance). In any case, it will be only a minor refactoring.
Probability-Driven Decisions
There is nothing wrong with the abstraction of every implementation with an interface if such an approach can be clearly justified, but interfaces become dubious when you have to introduce artificial naming conventions to avoid name clashes. Using prefixes such as “I” or suffixes such as IF or Impl should be considered to be code smells.
Interfaces should be introduced only as a contract for already existing classes, for the realization of Strategy or Bridge patterns, or when you need to design an API, such as Java Database Connectivity (JDBC), Java Message Service (JMS), and so on. In typical business applications, this occurs in only a fraction of all cases.
Usually injecting a class without any interface is good enough. In the unlikely, worst-case scenario, you will have to introduce an interface afterwards. Such partial refactorings are cheaper to perform afterwards than doubling the number of artifacts by implementing an interface for every significant class.
Binary incompatible changes are not an issue in enterprise applications either. In a Continuous Integration (CI) environment, all the code will be recompiled, retested, repackaged, and redeployed at every commit anyway. Reasoning that “it might be needed in the future” or implementing premature extensions for every possible use case is no longer viable in Java EE 6. It is trivial to extend well-written and concise code after the fact with only minor refactorings.
See Also
- Wikepedia: Strategy pattern
- Wikepedia: Bridge pattern
- Interface information in the Java Language Specification
- Interface definition: http://en.wikipedia.org/wiki/Java_interface
- EJB 3.1 specification (JSR 318)
- Real World Java EE Night Hacks—Dissecting the Business Tier (see “Easy Extensibility for the Unlikely Case” on page 102)
About the Author
Consultant and author Adam Bien is an Expert Group member for the Java EE 6 and 7, EJB 3.X, JAX-RS, CDI, and JPA 2.X JSRs. He has worked with Java technology since JDK 1.0 and with servlets/EJB 1.0 in several large-scale projects, and he is now an architect and developer for Java SE and Java EE projects. He has edited several books about JavaFX, J2EE, and Java EE, and he is the author of Real World Java EE Patterns—Rethinking Best Practices and Real World Java EE Night Hacks—Dissecting the Business Tier. Adam is also a Java Champion and JavaOne 2009 Rock Star.