Version 14

    Your first Seam application will be a very simple one where we do the following:

    • A name can be entered on a webpage and saved to a database

    • At any point, the user can press a button to go to a page which displays all the names currently saved

      • From this list page, names can be deleted or updated

      • The user can also go back to the entry page

     

    We will not be using the Seam-Gen tools at this point, asides from the initial project creation outline in the previous step.

     

    If you're not sure why we want to use EJB, I strongly suggest taking a quick break to brush up on it. 

     

    I'm not displaying the interfaces which the session beans are implementing.  There's nothing of interest in them, with the possible exception that that are annotated with

    @Local

    ; searching the Internet should provide many, many examples.

     

    -


    Create the Person entity

    package org.domain.myProject.entity;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    import org.hibernate.validator.NotNull;
    import org.jboss.seam.annotations.Name;
    
    @Entity
    @Name("person")
    public class Person {
    
         private Long id;
         private String name;
         
         @Id @GeneratedValue
         public Long getId() {return id;}
         public void setId(Long id) {this.id = id;}
         
         @NotNull
         public String getName() {return name;}
         public void setName(String name) {this.name = name;}        
    }
    
    • @Entity

      denotes that this is an EJB entity bean

    • @Name

      denotes that this is a Seam component

    • @Id

      denotes that this is the identification parameter for this entity (the primary key)

    • @GeneratedValue

      denotes that this value is auto-generated

    • @NotNull

      is a validation check, which states that this value can not be null (there are tricks to validation, keep reading)

     

    -


    Create the SavePersonAction stateless session bean (SLSB)

    package org.domain.myProject.session;
    
    import javax.ejb.Stateless;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    import org.domain.myProject.entity.Person;
    import org.domain.myProject.session.local.SavePersonLocal;
    import org.jboss.seam.annotations.In;
    import org.jboss.seam.annotations.Logger;
    import org.jboss.seam.annotations.Name;
    import org.jboss.seam.annotations.Out;
    import org.jboss.seam.faces.FacesMessages;
    import org.jboss.seam.log.Log;
    
    @Stateless
    @Name("savePerson")
    public class SavePersonAction implements SavePersonLocal {
    
         @Logger
         private Log log;
         
         @In
         FacesMessages facesMessages;
    
         @PersistenceContext
         private EntityManager em;
         
         /* We have an '@Out' on this to push back person (after any modifications we make
          * in our bean here) onto the page.  Since we're nullifying person in our save method, 
          * we need to state that it's not required to have any value.
          */  
         @In @Out(required=false)
         private Person person;
    
         public void save() {
              //Simple persisting with the EntityManager
              em.persist(person);
              
              //Note that the id of our person was generated and populated.  
              facesMessages.add("Person was saved with an id of "+ person.getId());
              
              log.info(person.getName() +" was saved.");
              
              //We're nullifying person so that the page is blank after saving.
              person = null;
         }
    }
    
    • @Stateless

      makes this class a stateless session bean

    • @Logger

      this causes Seam to bring up our logger, a nice short-cut to having to declare one ourselves

    • @In

      this has Seam inject (or pull) the component that has this annotation.  In the case of "facesMessages", Seam is going to bring in that specific component for our use.  That particular component is declared as part of the initial set-up.

    • @Out

      this has Seam outject (or push) the component that has this annotation. 

    • @PersistenceContext

      this tells Seam to use the below EntityManager as our persistence context for handling entities

    • The rest of this should be adequately explained by the in-class comments

     

    -


    Create the EditPersonForm stateful session bean (SFSB)

    
    
    package org.domain.myProject.session;
    
    import static javax.persistence.PersistenceContextType.EXTENDED;
    
    import java.util.List;
    
    import javax.ejb.Remove;
    import javax.ejb.Stateful;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    import org.domain.myProject.entity.Person;
    import org.domain.myProject.session.local.EditPersonLocal;
    import org.jboss.seam.ScopeType;
    import org.jboss.seam.annotations.Begin;
    import org.jboss.seam.annotations.Destroy;
    import org.jboss.seam.annotations.End;
    import org.jboss.seam.annotations.Factory;
    import org.jboss.seam.annotations.In;
    import org.jboss.seam.annotations.Logger;
    import org.jboss.seam.annotations.Name;
    import org.jboss.seam.annotations.Out;
    import org.jboss.seam.annotations.Scope;
    import org.jboss.seam.annotations.datamodel.DataModel;
    import org.jboss.seam.annotations.datamodel.DataModelSelection;
    import org.jboss.seam.faces.FacesMessages;
    import org.jboss.seam.log.Log;
    
    @Stateful
    @Name("editPerson")
    @Scope(ScopeType.CONVERSATION)
    public class EditPersonForm implements EditPersonLocal {
    
        @Logger 
        private Log log;
        
        @In 
        FacesMessages facesMessages;
        
        @PersistenceContext(type = EXTENDED)
        private EntityManager em;
     
        @DataModel
        private List<Person> people;
        
        @DataModelSelection
        @Out(required=false)
        private Person person;
         
        @SuppressWarnings("unchecked")
              @Factory("people")
              @Begin
        public void findPeople(){
             log.info("Finding the people");
             if (people == null)
                  log.info("people is null");
             else if (people.size() == 0)
                  log.info("people has a size of zero");
             else
                  log.info("Strange, because people has " +people.size() + " entries");
             
             people = em.createQuery("from Person u").getResultList();
        }
        
        public void remove(){
             log.info("Deleting " +person.getName());
             em.remove(person);
             people.remove(person);
        }
    
        @End
        public void clear(){
             //We're clearing person since it is being outjected and would otherwise populate the entry page.
             person = null;
        }
              
        public void update() {
             log.info("Updating " + person.getId());
             em.persist(person);
        }
        
        @Destroy @Remove                                                                      
        public void destroy() {}
    }
              
    
    • Here, we're using the

      CONVERSATION

      scope, which means that Seam will keep this bean in scope from the time we

      @Begin

      our "conversation", until the time that we

      @End

      it.  This can span multiple page, but for now we're just having it on the one. 

    • We've given our PersistenceContext the parameter of "

      (type = EXTENDED)

      " so that it will hang around longer then just one method

    • @DataModel

      tells Seam that the below list is going to be used as a data model (needed for our JSF, as you'll see)

    • @DataModelSelection

      tells Seam what we'll be selecting from the data model.  Needed for when we pull stuff back in for useage

    • @Factory

      when the component inside the factory (in this case, "people") is null, Seam will call this method.  As can be seen, we're using it to generate our data model.

    • The

      destroy()

      method at the bottom is needed by Seam to dispose of this SFSB.  Just note that all SFSB's have to have this.

    -


    Create the person.xhtml page

    <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
         xmlns:s="http://jboss.com/products/seam/taglib"
         xmlns:f="http://java.sun.com/jsf/core"
         xmlns:h="http://java.sun.com/jsf/html">
    
    <f:view>
         <h:form>
              <s:validateAll>
                   Name: 
                   <h:inputText value="#{person.name}" required="true" 
                        requiredMessage="You need to enter a name"></h:inputText>
                   <br />
                   <h:commandButton value="Save" action="#{savePerson.save()}" ></h:commandButton>
                   <s:button value ="List" view="/listPeople.xhtml"></s:button>
              </s:validateAll>
         </h:form>
    <h:messages ></h:messages>     
    </f:view>
    
    </html>
    
    

     

    • All JSF components have to go inside the

      <f:view>

      tag

    • <s:validateAll>

      tells Seam to run validation on all fields within this tag

      • Please note that there's a snag with JSF as of this writing: it does not run any validation against fields the user has left blank.

    • <h:inputText>

      is how we input values.  The

      "#{person.name}"

      is an example of expression language (EL) which we can use to call our Seam components, their parameters and their methods

      • We have

        required="true"

        on this because of the no-validation-on-blanks mentioned above.  Also keep in mind that the JSF page doesn't pass "null" on blanks, but instead an empty String.

    • <s:button>

      differs from

      <h:commandButton>

      primarily in that the

      <s:button>

      doesn't submit the form, it just calls an action and goes to a location.  We could have put this outside of the

      <h:form>

      and been fine.

    • <h:messages ></h:messages>

      displays any messages that we've put into the

      facesMessages

      component

    -


     

    Create the ~listPeople.xhtml page

    <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
         xmlns:s="http://jboss.com/products/seam/taglib"
         xmlns:f="http://java.sun.com/jsf/core"
         xmlns:h="http://java.sun.com/jsf/html">
    
    <f:view>
         <h:messages ></h:messages>
         <h:form>
         <h:dataTable value="#{people}" var="person">
              <h:column>
                   <f:facet name="header">
                        <h:outputText value="Id" ></h:outputText>
                   </f:facet>
                   <h:outputText value="#{person.id}" ></h:outputText>
              </h:column>
              <h:column>
                   <f:facet name="header">
                        <h:outputText value="Name" ></h:outputText>
                   </f:facet>
                   <h:inputText value="#{person.name}" ></h:inputText>
              </h:column>
              <h:column>
                   <f:facet name="header">
                        <h:outputText value="Actions" ></h:outputText>
                   </f:facet>
                   <h:commandButton action="#{editPerson.update()}" value="H. Update" ></h:commandButton>
                   <h:commandButton action="#{editPerson.remove()}" value="H. Delete" ></h:commandButton>
              </h:column>
         </h:dataTable>
         </h:form>
         <s:button action="#{editPerson.clear()}" view="/person.xhtml" value="S. Return"></s:button>
    </f:view>
    
    </html>
    
    
    • Here we are using the data model with

      people

      (our list) as the source.  Each entry in the table is referred to as "person"

    • The data table has columns (

      <h:column>

      ) and each column has variace facets (attributes).  In this case, we're setting the "header" facet for each column with the column name

     

    -


     

    And that's basically it. See the attached files for the complete project.  (Do not use the big zip file, it didn't upload correctly)

     

    The attached files show "user" instead of "person" (it's an old version), but I don't have permission to edit/remove the files.  You can change it yourself, or use them only as reference.

     

    Main