Monster Component in Java EE 7

With Java EE 7 being released I thought it was time to update the Monster Component ! A few years ago Ludovic Champenois had this idea of adding as many Java EE annotations as possible to a Java class. It was then used by Alexis Moussine Pouchkine on his Java EE seminars. It was time to have a revival and update the code to fit Java EE 7 novelties.

What is a Monster Component ?

A monster component is a useless piece of code to which you add several Java EE annotations giving it different aspects. Java EE is a managed environment : take a Java class, add a @Stateless annotation, and the container gives you transactions, security, pooling… take another class, add a @Path annotation and the container gives you REST invocation. So, if you add both @Stateless and @Path to the same class, you accumulate the services of a stateless EJB and a REST Web Service.

The Monster Component

The Book class below is a persistent class with a method (createAndListBooks) that persists itself and retrieves all the books from the database. It has been turned into a Monster Component because it accumulates several services:

  • Persistence (JPA) : the Book class is annotated with @Entity, @Table and declares a @NamedQuery. Some Book attributes customize the mapping (with @Column) or are marked as @Transient (such as the EntityManager)
  • XML binding (JAXB) : with the @XmlRootElement and @XmlAccessorType annotations, the Book class can use marshalization to get an XML representation of a Book. It customizes the XML binding using @XmlElement or @XmlTransient (for the EntityManager)
  • Constraints (Bean Validation) : some of the attributes of the Book have constraints (@Size) as well as method parameters (@NotNull on createAndListBooks)
  • EJB : the Book class is a stateless EJB (annotated with @Stateless), therefore it injects an EntityManager and declares the transactional method createAndListBooks
  • Servlet : the Book class is also a Servlet as it extends HttpServlet, is annotated with @WebServlet and overrides the doGet method. This method uses the injected Book EJB to persist itself.
  • RESTful Web Service (JAX-RS) : the @Path and @GET method allows you to invoke the createAndListBooks method via a HTTP GET. It produces an XML representation of all the books thanks to the JAXB @XmlRootElement annotation
  • Lifecycle management : two private methods have lifecycle management (@PostConstruct and @PreDestroy)
  • Interception : the Book class defines a method interceptor (method logMethod annotated with @AroundInvoke) that logs all method invocation

[sourcecode language=”java” highlight=”1,2,3,4,5,6,7,8,15,16,22,23,24,38,39,40,43,66,67,68,96,106,112″]
@Path("/MonsterRest")
@Stateless
@WebServlet(urlPatterns = "/MonsterServlet")
@Entity
@Table(name = "MonsterEntity")
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@NamedQuery(name = "findAll", query = "SELECT c FROM Book c")
public class Book extends HttpServlet {

// ======================================
// = Attributes =
// ======================================

@Id
@GeneratedValue
private Long id;
private String isbn;
private Integer nbOfPage;
private Boolean illustrations;
private String contentLanguage;
@Column(nullable = false)
@Size(min = 5, max = 50)
@XmlElement(nillable = false)
private String title;
private Float price;
@Column(length = 2000)
@Size(max = 2000)
private String description;
@ElementCollection
@CollectionTable(name = "tags")
private List<String> tags = new ArrayList<>();

// ======================================
// = Injected Resources =
// ======================================

@XmlTransient
@Transient
@EJB
private Book monsterEJB;

@XmlTransient
@Transient
@PersistenceContext(unitName = "monsterPU")
private EntityManager em;

// ======================================
// = Servlet Entry Point =
// ======================================

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String title = request.getParameter("title");
try {
response.getWriter().println("Servlet calling EJB " + monsterEJB.createAndListBooks(title));
} catch (Exception e) {
e.printStackTrace();
}
}

// ======================================
// = Business methods =
// ======================================

@GET
@Path("/{title}")
@Produces(MediaType.APPLICATION_XML)
public List<Book> createAndListBooks(@PathParam("title") @NotNull String title) {
// Sets data
this.id = null;
this.title = title + " " + new Date();
this.price = new Float(0.01);
this.description = "The hard-coded description";
this.isbn = "978-1-4302-1954-5";
this.nbOfPage = 210;
this.illustrations = Boolean.TRUE;
List<String> tags = new ArrayList<>();
tags.add("Monster");
tags.add("Component");
this.tags = tags;

// Persists the book
em.persist(this);

// Returns all books
TypedQuery<Book> query = em.createNamedQuery("findAll", Book.class);
List<Book> allBooks = query.getResultList();
return allBooks;
}

// ======================================
// = Interceptor =
// ======================================

@AroundInvoke
public Object logMethod(InvocationContext ic) throws Exception {
System.out.println(">>> " + ic.getTarget().getClass() + " – " + ic.getMethod().getName());
try {
return ic.proceed();
} finally {
System.out.println("<<< " + ic.getTarget().getClass() + " – " + ic.getMethod().getName());
}
}

@PostConstruct
private void prepare() {
System.out.println("\n=> PostConstruct");
System.out.println("================");
}

@PreDestroy
private void release() {
System.out.println("=============");
System.out.println("=> PreDestroy");
}

(…)
}
[/sourcecode]

Execute the Monster Component

First of all, if you download the code and run the integration test, you will notice that this code actually works ;o) I’ve added a simple JSF web page and a backing bean to invoke the Monster Component in several ways (EJB, Servlet and REST Web Service). So you can package everything in a war file and deploy it to GlassFish 4. There is also an Integration Test that checks it works in an embedded EJB container.

What’s missing ?

I could have added more annotations… but things don’t always work the way we want. I wanted to add SOAP Web Service (JAX-WS) capabilities with a @WebService and @WebMethod annotations. First of all, if you add a @WebService annotation, it makes the integration test fail (SOAP Web Services are not part of the embedded EJB container). I couldn’t also generate the WSDL, something wrong with the combination of JAX-WS and JPA mapping annotations. Also remember that JAX-WS is not part of the Java EE 7 Web Profile, and JAX-RS is.

The Servlet needs to inject the EJB. Impossible to use the CDI @Inject annotation because it is a cyclic reference (the Book injects itself). But with @EJB it works. So I didn’t add CDI in this example.

Do you see anything else that could be added ?

What does this show ?

This example of code shows that you don’t have to over engineer your code and add multiple decoupled architectural layers. Just put everything into a single class ;o) This code is a bit shocking, several concepts are embedded in a single class (no separation of concerns) and most of you (including me) will find this ugly. On the other hand, having several layers, abstraction, interfaces, DAOs, DTOs and so on… is also ugly (but it looks like we got use to such complexity). Don’t put everything into a single class, but do not spread a concern over several artifacts either. Find the right balance and KISS.

Conclusion

I’ve shown you in this post that the Java EE 7 philosophy is just about adding metadata to a Java class and leave the container to do the job. Metadata can be defined with XML or annotations. The Book class has be turned into a Monster Component by adding as many annotations as possible. The class can persist itself in a transactional manner thanks to JPA and EJB, as well as being invoked as a Servlet and RESTful Web Service, while intercepting each method invocation and validating constraints.

5 thoughts on “Monster Component in Java EE 7

  1. Thanks for the 2009 MONSTER reference!

    By the way, doing more and more Cloud development, I think the fact that the Java EE team (and I am guilty) has moved more and more annotation processing at the container level when it starts as opposed as being pre-processed at the SDK level during development is a major design failure! Booting 100k of JVMs (or more in case of Google App Engine) on the cloud, each doing its own annotation processing is a cloud disaster in terms of power energy lost doing over and over the same crappy thing at boot time and it terms of massive scalability… One thing that would be nice to improve for Java EE 8 if ever there is one. A lot of things to be done before being Cloud Ready and power efficient for the massive Data Centers.

  2. Nice post!

    “This example of code shows that you don’t have to over engineer your code and add multiple decoupled architectural layers. Just put everything into a single class” ohhh yeahhh!!! xD
    A nice follow-up would show how to “find the right balance and KISS”!!!

  3. OMG.. Really interesting example. Simply great. But I definately wouldn’t implement such a monster in a real application..

Leave a Reply