[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—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><s:validateAll></literal> and <literal><s:decorate></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