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)

class is annotated with , and declares a . Some attributes customize the mapping (with ) or are marked as (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 )

and annotations, the class can use marshalization to get an XML representation of a . It customizes the XML binding using or (for the ) Constraints (Bean Validation) : some of the attributes of the Book have constraints ( @Size ) as well as method parameters ( @NotNull on createAndListBooks )

) as well as method parameters ( on ) EJB : the Book class is a stateless EJB (annotated with @Stateless ), therefore it injects an EntityManager and declares the transactional method createAndListBooks

class is a stateless EJB (annotated with ), therefore it injects an and declares the transactional method 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.

, is annotated with and overrides the 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

and method allows you to invoke the method via a HTTP GET. It produces an XML representation of all the books thanks to the JAXB annotation Lifecycle management : two private methods have lifecycle management ( @PostConstruct and @PreDestroy )

and ) Interception : the Book class defines a method interceptor (method logMethod annotated with @AroundInvoke ) that logs all method invocation

@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("

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

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