[jboss-cvs] jboss-seam/doc/reference/en/modules ...

Gavin King gavin.king at jboss.com
Tue Oct 17 16:48:18 EDT 2006


  User: gavin   
  Date: 06/10/17 16:48:18

  Added:       doc/reference/en/modules  framework.xml
  Log:
  doc the fwk
  
  Revision  Changes    Path
  1.1      date: 2006/10/17 20:48:18;  author: gavin;  state: Exp;jboss-seam/doc/reference/en/modules/framework.xml
  
  Index: framework.xml
  ===================================================================
  <chapter id="framework">
      <title>The Seam Application Framework</title>
      
      <para>
          Seam makes it really easy to create applications by writing
          plain Java classes with annotations, which don't need to extend
          any special interfaces or superclasses. But we can simplify
          some common programming tasks even further, by providing a set 
          of pre-built components which can be re-used either by
          configuration in <literal>components.xml</literal> (for very 
          simple cases) or extension.
      </para>
      
      <para>
          The <emphasis>Seam Application Framework</emphasis> can reduce
          the amount of code you need to write when doing basic database
          access in a web application, using either Hibernate or JPA.
      </para>
      
      <para>
          We should emphasize that the framework is extremely simple, 
          just a handful of simple classes that are easy to understand
          and extend. The "magic" is in Seam itself&mdash;the same magic
          you use when creating any Seam application even without using 
          this framework.
      </para>
      
      <section>
          <title>Introduction</title>
          
          <para>
              The components provided by the Seam application framework
              may be used in one of two different approaches. The first
              way is to install and configure an instance of the component
              in <literal>components.xml</literal>, just like we have
              done with other kinds of built-in Seam components. For
              example, the following fragment from
              <literal>components.xml</literal> installs a component
              which can perform basic CRUD operations for a
              <literal>Contact</literal> entity:
          </para>
          
          <programlisting><![CDATA[<component name="personHome" class="org.jboss.seam.framework.EntityHome">
      <property name="entityClass">eg.Person</property>
      <property name="id">#{param.personId}</property>
      <property name="entityManager">#{personDatabase}</property>
  </component>]]></programlisting>
  
          <para>
              If that looks a bit too much like "programming in XML" for 
              your taste, you can use extension instead:
          </para>
  
          <programlisting><![CDATA[@Stateful
  @Name("personHome")
  public class PersonHome extends EntityHome<Person> {
      @RequestParameter Long id;
      @In(create=true) EntityManager personDatabase;
      
      public Object getId() { return id; }
      public EntityManager getEntityManager() { return personDatabase; }
      
  }]]></programlisting>
  
          <para>
              The second approach has one huge advantage: you can easily add 
              extra functionality, and override the built-in functionality
              (the framework classes were carefully designed for extension
              and customization).
          </para>
          
          <para>
              A second advantage is that your classes may be EJB stateful
              sessin beans, if you like. (They do not have to be, they 
              can be plain JavaBean components if you prefer.)
          </para>
          
          <para>
              At this time, the Seam Application Framework provides just four
              built-in components: <literal>EntityHome</literal> and
              <literal>HibernateEntityHome</literal> for CRUD, along with
              <literal>EntityQuery</literal> and <literal>HibernateEntityQuery</literal>
              for queries.
          </para>
          
      </section>
      
      <section>
          <title>Home objects</title>
          
          <para>
              A Home object provides persistence operations for a particular entity
              class. Suppose we have our trusty <literal>Person</literal> class:
          </para>
          
          <programlisting><![CDATA[@Entity
  public class Person {
      private Long id;
      private String firstName;
      private String lastName;
      private Country nationality;
      
      //getters and setters...
  }]]></programlisting>
  
          <para>
              We can define a <literal>personHome</literal> component either via
              configuration:
          </para>
          
          <programlisting><![CDATA[<component name="personHome" class="org.jboss.seam.framework.EntityHome">
      <property name="entityClass">eg.Person</property>
  </component>]]></programlisting>
  
          <para>
              Or via extension:
          </para>
  
          <programlisting><![CDATA[@Name("personHome")
  public class PersonHome extends EntityHome<Person> {}]]></programlisting>
  
          <para>
              A Home object provides the following operations: <literal>persist()</literal>,
              <literal>remove()</literal>, <literal>update()</literal> and 
              <literal>getInstance()</literal>. Before you can call the 
              <literal>remove()</literal>, or <literal>update()</literal> operations, you 
              must first set the identifier of the object you are interested in, using the 
              <literal>setId()</literal> method.
          </para>
          
          <para>
              We can use a Home directly from a JSF page, for example:
          </para>
          
          <programlisting><![CDATA[<h1>Create Person</h1>
  <h:form>
      <div>First name: <h:inputText value="#{personHome.instance.firstName}"/></div>
      <div>Last name: <h:inputText value="#{personHome.instance.lastName}"/></div>
      <div>
          <h:commandButton value="Create Person" action="#{personHome.persist}"/>
      </div>
  </h:form>]]></programlisting>
  
          <para>
              Usually, it is much nicer to be able to refer to the <literal>Person</literal>
              merely as <literal>person</literal>, so let's make that possible by adding a
              line to <literal>components.xml</literal>:
          </para>
          
          <programlisting><![CDATA[<factory name="person" value="#{personHome.instance}"/>
  
  <component name="personHome" class="org.jboss.seam.framework.EntityHome">
      <property name="entityClass">eg.Person</property>
  </component>]]></programlisting>
  
          <para>
              (If we are using configuration.)
              Or by adding a <literal>@Factory</literal> method to <literal>PersonHome</literal>:
          </para>
          
          <programlisting><![CDATA[@Name("personHome")
  public class PersonHome extends EntityHome<Person> {
      
      @Factory("person")
      public Person initPerson() { return getInstance(); }
      
  }]]></programlisting>
          
          <para>
              (If we are using extension.) 
              This change simplifies our JSF page to the following:
          </para>
  
          <programlisting><![CDATA[<h1>Create Person</h1>
  <h:form>
      <div>First name: <h:inputText value="#{person.firstName}"/></div>
      <div>Last name: <h:inputText value="#{person.lastName}"/></div>
      <div>
          <h:commandButton value="Create Person" action="#{personHome.persist}"/>
      </div>
  </h:form>]]></programlisting>
  
          <para>
              Well, that lets us create new <literal>Person</literal> entries. Yes,
              that is all the code that is required! Now, if we want to be able to 
              display, update and delete pre-existing <literal>Person</literal> 
              entries in the database, we need to be able to pass the entry
              identifier to the <literal>PersonHome</literal>. Page parameters
              are a great way to do that:
          </para>
  
          <programlisting><![CDATA[<pages>
      <page viewid="/editPerson.jsp">
          <param name="personId">#{personHome.id}</param>
      </page>
  </pages>]]></programlisting>
  
          <para>
              Now we can add the extra operations to our JSF page:
          </para>
  
          <programlisting><![CDATA[<h1>
      <h:outputText rendered="#{!personHome.managed}" value="Create Person"/>
      <h:outputText rendered="#{personHome.managed}" value="Edit Person"/>
  </h1>
  <h:form>
      <div>First name: <h:inputText value="#{person.firstName}"/></div>
      <div>Last name: <h:inputText value="#{person.lastName}"/></div>
      <div>
          <h:commandButton value="Create Person" action="#{personHome.persist}" rendered="#{!personHome.managed}"/>
          <h:commandButton value="Update Person" action="#{personHome.update}" rendered="#{personHome.managed}"/>
          <h:commandButton value="Delete Person" action="#{personHome.remove}" rendered="#{personHome.managed}"/>
      </div>
  </h:form>]]></programlisting>
  
          <para>
              When we link to the page with no request parameters, the page will 
              be displayed as a "Create Person" page. When we provide a value for
              the <literal>personId</literal> request parameter, it will be an
              "Edit Person" page.
          </para>
          
          <para>
              Suppose we need to create <literal>Person</literal> entries with their
              nationality initialized. We can do that easily, via configuration:
          </para>
  
          <programlisting><![CDATA[<factory name="person" value="#{personHome.instance}"/>
  
  <component name="personHome" class="org.jboss.seam.framework.EntityHome">
      <property name="entityClass">eg.Person</property>
      <property name="newInstance">#{newPerson}</property>
  </component>
  
  <component name="newPerson" class="eg.Person">
      <property name="nationality">#{country}</property>
  </component>]]></programlisting>
           
           <para>
               Or by extension:
           </para>
           
          <programlisting><![CDATA[@Name("personHome")
  public class PersonHome extends EntityHome<Person> {
      
      @In Country country;
      
      @Factory("person")
      public Person initPerson() { return getInstance(); }
      
      protected Person getNewInstance() {
          return new Person(country);
      }
      
  }]]></programlisting>
  
          <para>
              Of course, the <literal>Country</literal> could be an object managed by
              another Home object, for example, <literal>CountryHome</literal>.
          </para>
          
          <para>
              To add more sophisticated operations (association management, etc), we can
              just add methods to <literal>PersonHome</literal>.
          </para>
  
          <programlisting><![CDATA[@Name("personHome")
  public class PersonHome extends EntityHome<Person> {
      
      @In Country country;
      
      @Factory("person")
      public Person initPerson() { return getInstance(); }
      
      protected Person getNewInstance() {
          return new Person(country);
      }
      
      public void migrate()
      {
          getInstance().setCountry(country);
          update();
      }
      
  }]]></programlisting>
  
          <para>
              The Home object automatically displays faces messages when an operation is
              successful. To customize these messages we can, again, use configuration:
          </para>
           
          <programlisting><![CDATA[<factory name="person" value="#{personHome.instance}"/>
  
  <component name="personHome" class="org.jboss.seam.framework.EntityHome">
      <property name="entityClass">eg.Person</property>
      <property name="newInstance">#{newPerson}</property>
      <property name="createdMessage">New person #{person.firstName} #{person.lastName} created</property>
      <property name="deletedMessage">Person #{person.firstName} #{person.lastName} deleted</property>
      <property name="updatedMessage">Person #{person.firstName} #{person.lastName} updated</property>
  </component>
  
  <component name="newPerson" class="eg.Person">
      <property name="nationality">#{country}</property>
  </component>]]></programlisting>
           
           <para>
               Or extension:
           </para>
           
          <programlisting><![CDATA[@Name("personHome")
  public class PersonHome extends EntityHome<Person> {
      
      @In Country country;
      
      @Factory("person")
      public Person initPerson() { return getInstance(); }
      
      protected Person getNewInstance() {
          return new Person(country);
      }
      
      protected String getCreatedMessage() { return "New person #{person.firstName} #{person.lastName} created"; }
      protected String getUpdatedMessage() { return "Person #{person.firstName} #{person.lastName} updated"; }
      protected String getDeletedMessage() { return "Person #{person.firstName} #{person.lastName} deleted"; }
      
  }]]></programlisting>
  
          <para>
              The final step is to add validation functionality to the page, using
              <literal>&lt;s:validateAll&gt;</literal> and <literal>&lt;s:decorate&gt;</literal>,
              but I'll leave that for you to figure out.
          </para>
  
      </section>
      
      <section>
          <title>Query objects</title>
          
          <para>
              If we need a list of all <literal>Person</literal> instance in the database, we
              can use a Query object. For example:
          </para>
          
          <programlisting><![CDATA[<component name="people" class="org.jboss.seam.framework.EntityQuery">
      <property name="ejbql">select p from Person p</property>
  </component>]]></programlisting>
          
          <para>
              We can use it from a JSF page:
          </para>
          
          <programlisting><![CDATA[<h1>List of people</h1>
  <h:dataTable value="#{people}" var="person">
      <h:column>
          <s:link view-id="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">
              <f:param name="personId" value="#{person.id}"/>
          </s:link>
      </h:column>
  </h:dataTable>]]></programlisting>
  
          <para>
              We probably need to support pagination:
          </para>
          
          <programlisting><![CDATA[<component name="people" class="org.jboss.seam.framework.EntityQuery">
      <property name="ejbql">select p from Person p</property>
      <property name="order">lastName</property>
      <property name="maxResults">20</property>
  </component>]]></programlisting>
  
          <para>
              The JSF code for a pagination control is a bit verbose, but manageable:
          </para>
  
          <programlisting><![CDATA[<h1>Search for people</h1>
  <h:dataTable value="#{people.resultList}" var="person">
      <h:column>
          <s:link view-id="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">
              <f:param name="personId" value="#{person.id}"/>
          </s:link>
      </h:column>
  </h:dataTable>
  
  <s:link view="/search.xhtml" rendered="#{people.previousExists}" value="First Page">
      <f:param name="firstResult" value="0"/>
  </s:link>
  
  <s:link view="/search.xhtml" rendered="#{people.previousExists}" value="Previous Page">
      <f:param name="firstResult" value="#{people.previousFirstResult}"/>
  </s:link>
  
  <s:link view="/search.xhtml" rendered="#{people.nextExists}" value="Next Page">
      <f:param name="firstResult" value="#{people.nextFirstResult}"/>
  </s:link>
  
  <s:link view="/search.xhtml" rendered="#{people.nextExists}" value="Last Page">
      <f:param name="firstResult" value="#{people.lastFirstResult}"/>
  </s:link>]]></programlisting>
  
          <para>
              Real search screens let the user enter a bunch of optional search criteria
              to narrow the list of results returned. The Query object lets you specify
              optional "restrictions" to support this important usecase:
          </para>
  
          <programlisting><![CDATA[<component name="examplePerson" class="Person"/>
          
  <component name="people" class="org.jboss.seam.framework.EntityQuery">
      <property name="ejbql">select p from Person p</property>
      <property name="order">lastName</property>
      <property name="maxResults">20</property>
      <restrictions>
          <value>lower(firstName) like lower( #{examplePerson.firstName} + '%' )</value>
          <value>lower(lastName) like lower( #{examplePerson.lastName} + '%' )</value>
      </restriction>
  </component>]]></programlisting>
  
          <para>
              Notice the use of an "example" object.
          </para>
  
          <programlisting><![CDATA[<h1>Search for people</h1>
  <h:form>
      <div>First name: <h:inputText value="#{examplePerson.firstName}"/></div>
      <div>Last name: <h:inputText value="#{examplePerson.lastName}"/></div>
      <div><h:commandButton value="Search" action="/search.jsp"/></div>
  </h:form>
  
  <h:dataTable value="#{people.resultList}" var="person">
      <h:column>
          <s:link view-id="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">
              <f:param name="personId" value="#{person.id}"/>
          </s:link>
      </h:column>
  </h:dataTable>]]></programlisting>
  
          <para>
              The examples in this section have all shown reuse by configuration. However,
              reuse by extension is equally possible for Query objects.
          </para>
  
      </section>
      
  </chapter>
  
  



More information about the jboss-cvs-commits mailing list