2.10.3. Using "push"-style MVC in a RESTful application
Very occasionally, it makes more sense to use push-style MVC for processing RESTful pages, and so Seam provides the notion of a
page action
. The Blog example uses a page action for the blog entry page, entry.xhtml
. Note that this is a little bit contrived, it would have been easier to use pull-style MVC here as well.
The entryAction
component works much like an action class in a traditional push-MVC action-oriented framework like Struts:
@Name("entryAction")
@Scope(STATELESS)
public class EntryAction
{
@In(create=true)
private Blog blog;
@Out
private BlogEntry blogEntry;
public void loadBlogEntry(String id) throws EntryNotFoundException
{
blogEntry = blog.getBlogEntry(id);
if (blogEntry==null) throw new EntryNotFoundException(id);
}
}
Example 2.33.
Page actions are also declared in pages.xml
:
<pages>
...
<page view-id="/entry.xhtml" action="#{entryAction.loadBlogEntry(blogEntry.id)}">
<param name="blogEntryId" value="#{blogEntry.id}"/>
</page>
<page view-id="/post.xhtml" action="#{loginAction.challenge}"/>
<page view-id="*" action="#{blog.hitCount.hit}"/>
</pages>
Example 2.34.
Notice that the example is using page actions for some other functionality—the login challenge, and the pageview counter. Also notice the use of a parameter in the page action method binding. This is not a standard feature of JSF EL, but Seam lets you use it, not just for page actions, but also in JSF method bindings.
When the entry.xhtml
page is requested, Seam first binds the page parameter blogEntryId
to the model, then runs the page action, which retrieves the needed data—the blogEntry
—and places it in the Seam event context. Finally, the following is rendered:
<div class="blogEntry">
<h3>#{blogEntry.title}</h3>
<div>
<h:outputText escape="false" value="#{blogEntry.body}"/>
</div>
<p>
[Posted on
<h:outputText value="#{blogEntry.date}">
<f:convertDateTime timezone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>
</h:outputText>]
</p>
</div>
Example 2.35.
If the blog entry is not found in the database, the EntryNotFoundException
exception is thrown. We want this exception to result in a 404 error, not a 505, so we annotate the exception class:
@ApplicationException(rollback=true)
@HttpError(errorCode=HttpServletResponse.SC_NOT_FOUND)
public class EntryNotFoundException extends Exception
{
EntryNotFoundException(String id)
{
super("entry not found: " + id);
}
}
Example 2.36.
An alternative implementation of the example does not use the parameter in the method binding:
@Name("entryAction")
@Scope(STATELESS)
public class EntryAction
{
@In(create=true)
private Blog blog;
@In @Out
private BlogEntry blogEntry;
public void loadBlogEntry() throws EntryNotFoundException
{
blogEntry = blog.getBlogEntry( blogEntry.getId() );
if (blogEntry==null) throw new EntryNotFoundException(id);
}
}
<pages>
...
<page view-id="/entry.xhtml" action="#{entryAction.loadBlogEntry}">
<param name="blogEntryId" value="#{blogEntry.id}"/>
</page>
...
</pages>
Example 2.37.
It is a matter of taste which implementation you prefer.