The JBoss Maven project in detail

Let's see how the base project is composed. Basically it's a simple application which is used to register user names with a simple JSF form and can query the list of users which are also exposed through a REST interface.

The EJB 3 MemberRegistration Stateful bean is marked with the @Model annotation. This annotation does two things:

  • It makes the bean request-scoped (instead of dependent, the default)
  • It gives the bean an EL name


(Please note that creating a request-scoped and named bean is typically accomplished by apply the annotations @RequestScoped and @Named to the class, respectively. Since the combination of these annotations is so common in web applications, the built-in stereotype annotation @Model is provided by CDI as a shorthand).

package com.mastertheboss.controller;

import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.Stateful;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Model;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import com.mastertheboss.model.Member;

@Stateful
@Model
public class MemberRegistration {

   @Inject
   private Logger log;

   @Inject
   private EntityManager em;

   @Inject
   private Event<Member> memberEventSrc;

   private Member newMember;

   @Produces
   @Named
   public Member getNewMember() {
      return newMember;
   }

   public void register() throws Exception {
      log.info("Registering " + newMember.getName());
      em.persist(newMember);
      memberEventSrc.fire(newMember);
      initNewMember();
   }

   @PostConstruct
   public void initNewMember() {
      newMember = new Member();
   }
}

The MemberListProducer is responsible for instantiating Members objects. As per specs, the getMembers producer method acts as a source of objects to be injected.

Notice we have added an event observer (@Observes) to the MemberListProducer just to see the events are still being raised and can be observed. This will retrieve all members stored as soon as one member has changed.
Events are a great way to decouple parts of the system in a modular fashion as you can add pieces that will subscribe to events with the event producer unaware of the observer as opposed to the even producer having to call the observer manually when events are not used.

Finally, the @Named annotation provides access the return value via the EL variable name "members" in the UI.

package com.mastertheboss.data;

import com.mastertheboss.model.Member;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;

import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

@RequestScoped
public class MemberListProducer {
   @Inject
   private EntityManager em;

   private List<Member> members;

   @Produces
   @Named
   public List<Member> getMembers() {
      return members;
   }

   public void onMemberListChanged(@Observes(notifyObserver = Reception.IF_EXISTS) final Member member) {
      retrieveAllMembersOrderedByName();
   }

   @PostConstruct
   public void retrieveAllMembersOrderedByName() {
      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<Member> criteria = cb.createQuery(Member.class);
      Root<Member> member = criteria.from(Member.class);

      criteria.select(member).orderBy(cb.asc(member.get("name")));
      members = em.createQuery(criteria).getResultList();
   }
}

This is the Member model class which incarnates the database Member table. Some quick recap about the annotations used here: The @UniqueConstraint specifies that a unique constraint is to be included in the generated DDL for a primary or secondary table. In this case, the email field must be unique. The @Pattern annotation, whose property must match the regular expression defined in the regexp element. The @Size of the field or property is evaluated and must match the specified boundaries. The @Digits means that the value of the field or property must be a number within a specified range.

package com.mastertheboss.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
@XmlRootElement
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"))
public class Member implements Serializable {
   /** Default value included to remove warning. Remove or modify at will. **/
   private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue
   private Long id;

   @NotNull
   @Size(min = 1, max = 25)
   @Pattern(regexp = "[A-Za-z ]*", message = "must contain only letters and spaces")
   private String name;

   @NotNull
   @NotEmpty
   @Email
   private String email;

   @NotNull
   @Size(min = 10, max = 12)
   @Digits(fraction = 0, integer = 12)
   @Column(name = "phone_number")
   private String phoneNumber;

   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getEmail() {
      return email;
   }

   public void setEmail(String email) {
      this.email = email;
   }

   public String getPhoneNumber() {
      return phoneNumber;
   }

   public void setPhoneNumber(String phoneNumber) {
      this.phoneNumber = phoneNumber;
   }
}

The JaxRsActivator class extends javax.ws.rs.core.Application so that is automatically enlisted into JAX-RS resources and served relative to the servlet path specified in the @ApplicationPath annotation.

package com.mastertheboss.rest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
   /* class body intentionally left blank */
}

Next, the MemberResourceRESTService class produces a RESTful service to read the contents of the members table. It contains two methods which return via XML the list of all Members and a member lookup by id.

package com.mastertheboss.rest;

import java.util.List;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import com.mastertheboss.model.Member;

@Path("/members")
@RequestScoped
public class MemberResourceRESTService {
   @Inject
   private EntityManager em;

   @GET
   @Produces("text/xml")
   public List<Member> listAllMembers() {

      @SuppressWarnings("unchecked")
      final List<Member> results = em.createQuery("select m from Member m order by m.name").getResultList();
      return results;
   }

   @GET
   @Path("/{id:[0-9][0-9]*}")
   @Produces("text/xml")
   public Member lookupMemberById(@PathParam("id") long id) {
      return em.find(Member.class, id);
   }
}

Finally, in the Resources class, the @Produces annotation identifies the produceLog() method as a producer method. A producer method is called whenever another bean in the system needs an injected object of the specified type. In this example this method is used to alias the Logger resource using CDI.

package com.mastertheboss.util;

import java.util.logging.Logger;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;


public class Resources {

   @SuppressWarnings("unused")
   @Produces
   @PersistenceContext
   private EntityManager em;
   
   @Produces
   public Logger produceLog(InjectionPoint injectionPoint) {
      return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
   }
}

0
0
0
s2sdefault

Related articles available on mastertheboss.com