example.xml 5.16 KB
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"  [ ]>
<chapter id="example">
   <title>JSF web application example</title>

   <para>
      Let's illustrate these ideas with a full example. We're going to implement user login/logout for an application
      that uses JSF. First, we'll define a request-scoped bean to hold the username and password entered during login, 
      with constraints defined using annotations from the Bean Validation specification:
   </para>

<programlisting role="JAVA"><![CDATA[@Named @RequestScoped
public class Credentials {
    private String username;
    private String password;
    
    @NotNull @Length(min=3, max=25)
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    @NotNull @Length(min=6, max=20)
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}]]></programlisting>

   <para>This bean is bound to the login prompt in the following JSF form:</para>

   <programlisting role="JAVA"><![CDATA[<h:form>
   <h:panelGrid columns="2" rendered="#{!login.loggedIn}">
      <f:validateBean>
         <h:outputLabel for="username">Username:</h:outputLabel>
         <h:inputText id="username" value="#{credentials.username}"/>
         <h:outputLabel for="password">Password:</h:outputLabel>
         <h:inputSecret id="password" value="#{credentials.password}"/>
      </f:validateBean>
   </h:panelGrid>
   <h:commandButton value="Login" action="#{login.login}" rendered="#{!login.loggedIn}"/>
   <h:commandButton value="Logout" action="#{login.logout}" rendered="#{login.loggedIn}"/>
</h:form>]]></programlisting>

      <para>
         Users are represented by a JPA entity:
      </para>
      
      <programlisting role="JAVA"><![CDATA[@Entity
public class User {
   private @NotNull @Length(min=3, max=25) @Id String username;
   private @NotNull @Length(min=6, max=20) String password;
   
   public String getUsername() { return username; }
   public void setUsername(String username) { this.username = username; }
   public String setPassword(String password) { this.password = password; }
}]]></programlisting>

      <para>
         (Note that we're also going to need a <literal>persistence.xml</literal> file to configure the JPA persistence 
         unit containing <literal>User</literal>.)
      </para>

      <para>
         The actual work is done by a session-scoped bean that maintains information about the currently logged-in user
         and exposes the <literal>User</literal> entity to other beans:
      </para>

      <programlisting role="JAVA"><![CDATA[@SessionScoped @Named
public class Login implements Serializable {

   @Inject Credentials credentials;
   @Inject @UserDatabase EntityManager userDatabase;

   private User user;
    
   public void login() {
      List<User> results = userDatabase.createQuery(
         "select u from User u where u.username = :username and u.password = :password")
         .setParameter("username", credentials.getUsername())
         .setParameter("password", credentials.getPassword())
         .getResultList();
        
      if (!results.isEmpty()) {
         user = results.get(0);
      }
      else {
         // perhaps add code here to report a failed login
      }
   }
    
   public void logout() {
      user = null;
   }
    
   public boolean isLoggedIn() {
      return user != null;
   }
    
   @Produces @LoggedIn User getCurrentUser() {
      return user;
   }

}]]></programlisting>

   <para><literal>@LoggedIn</literal> and <literal>@UserDatabase</literal> are custom qualifier annotations:</para>

   <programlisting role="JAVA"><![CDATA[@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, PARAMETER, FIELD})
public @interface LoggedIn {}]]></programlisting>

   <programlisting role="JAVA"><![CDATA[@Qualifier
@Retention(RUNTIME)
@Target({METHOD, PARAMETER, FIELD})
public @interface UserDatabase {}]]></programlisting>

   <para>
      We need an adaptor bean to expose our typesafe <literal>EntityManager</literal>:
   </para>

   <programlisting role="JAVA"><![CDATA[class UserDatabaseProducer {
   @Produces @UserDatabase @PersistenceContext 
   static EntityManager userDatabase;
}]]></programlisting> 
   
   <para>Now <literal>DocumentEditor</literal>, or any other bean, can easily inject the current user:</para>

   <programlisting role="JAVA"><![CDATA[public class DocumentEditor {
   @Inject Document document;
   @Inject @LoggedIn User currentUser;
   @Inject @DocumentDatabase EntityManager docDatabase;
    
   public void save() {
      document.setCreatedBy(currentUser);
      docDatabase.persist(document);
   }
}]]></programlisting>

   <para>Or we can reference the current user in a JSF view:</para>

   <programlisting role="XML"><![CDATA[<h:panelGroup rendered="#{login.loggedIn}">
   signed in as #{currentUser.username}
</h:panelGroup>]]></programlisting> 

   <para>
      Hopefully, this example gave you a taste of the CDI programming model. In the next chapter, we'll explore 
      dependency injection in greater depth.
   </para>

<!--
vim:et:ts=3:sw=3:tw=120
-->
</chapter>