Chapter 3. The Seam Application

Table of Contents

3.1. Initializing the project
3.2. Seam State Management
3.3. Getting Started
3.4. Editor Pages
3.5. Handling Navigation
3.6. The Seam Application Framework
3.7. Issues Viewer and Editor
3.8. Selecting Projects
3.9. The Seam Framwork Approach
3.10. Other Features

3.1. Initializing the project

The Seam application is being developed using a clean eclipse and the latest JBoss Tools plugins. Getting started with the project is a breeze. We start by defining the database connection and the JBoss app server to use in our project wizard. We start the project wizard which asks questions about the project, any datasources, deployment options, and server runtimes and it creates everything you need to create a new project.

Figure 3.1. Configuring the project Wizard

Configuring the project Wizard

Figure 3.2. Configuring Seam and the DB Connection

Configuring Seam and the DB Connection

Since I chose the EAR deployment over the WAR deployment, we end up with a set of projects:

  • The WAR project
  • The EJB project
  • The EAR Project that it is all wrapped up in
  • The Test Project

We have no jars to copy over, and no configuration files to set up, the project is ready to deploy and run. JBoss have done really well smoothing this piece out and letting users get up and running quickly.

3.2. Seam State Management

In Seam, state management works a little differently. We don’t need to put every page into a work flow just to manage state. With Seam, state management is always present in the form of conversations. Conversations can either be default or long running. The default conversation lasts just for this page and into the next page. A long running conversation lasts until you explicitly end it. All pages run in a conversation whether it is the default temporary conversation or whether it is a long running conversation. While it may seem intrusive, it is rarely noticeable and is actually a good thing as we will see later.

Once we start a long running conversation, any conversationally scoped data that is available in the conversation at the point the conversation started, as well as any conversational scoped data we add to the conversation, will remain in that conversation until it ends.

Conversations can be started in a number of ways, by calling methods on Seam components annotated with a @begin , or by using page metadata in pages.xml or from a link using the s:link or s:button Seam JSF components which have a propagation value set to true .

First off, we’ll be using the same model as the Spring sample project. Regarding the Dao pattern of layering, Seam offers a few different ways of structuring your application. Seam tends towards using ‘thicker’ stateful objects, objects that hold references to stateful data as well as providing action methods on those objects.

Seam also comes with classes for the "Seam Application Framework" which is a built-in framework for creating simple persistence and query components. We’ll cover both options here since it really is almost two separate features. There’s also the question of whether we use EJB or POJOs since Seam supports both out of the box. The draw back to this is that even if you use POJOs, you still require an EJB 3.0 environment to run it, even if it is the embedded EJB environment in a non-EJB container.

Seam works on a pull model rather than a push model. When a context variable is requested, Seam searches for the variable in the available contexts such as request scope, page scope, the conversational scope, and then session and application scopes. If it does not find it, it looks into its internal metadata to see if a factory method is registered for the variable name. If so, the factory method is called, and the variable is put into whatever scope the factory method or outjection specified.

3.3. Getting Started

We’ll start with our list of projects, for which we will create a ProjectListActionBean which will be an EJB. This bean will use a query for projects, and will be used for other functions later on. First we define an interface for the bean.

Example 3.1. EJB Local interface for our bean

					
@Local
public interface ProjectListAction {

	List<Project> getProjects();
    void remove();

}
				

We implement this interface in the class ProjectListBean and define it as a Seam component called projectList .

Example 3.2. Implementation of our stateful EJB

					
@Name("projectList")
@Scope(ScopeType.CONVERSATION)
@Stateful
public class ProjectListBean implements ProjectList, Serializable {

	@Logger
	private Log log;

	@In
	private EntityManager entityManager;
	
	private List<Project> projects;

	@SuppressWarnings("unchecked")
	@Factory("projects")
	public List<Project> getProjects() {
		if (projects == null) {
			log.debug("Getting projects");
			projects = entityManager.createQuery("select p from Project p")
			.getResultList();
		}
		return projects;
	}

	@Remove @Destroy
	public void remove() {
		
	}
}
				

Here we declare our ProjectListBean class to be a Seam component called projectList and it is declared as a stateful EJB. The @Name annotation is used to specify the name of the component to Seam and the @Stateful annotation is used to indicate that it is a stateful EJB. The @Factory annotation on the getProjects() method is used to declare a factory method in Seam. If a variable called projects is requested and not found, Seam would call this method and the results will be put into a context variable called projects . The scope of the results depends on the scope specified in the factory annotation if there is one. If not, it defaults to the scope of the component that contains the factory method (in this case, conversational scope).

The entity manager is a special seam component that is injected like other seam components. The @Logger annotated member is a Seam logger which is a standard logger that is automatically injected without the explicit code to obtain a logger per class type. It also wraps calls to the actual logger and lets you use EL syntax within the logged message such as "Saving project #{project.title}" and the values will be substituted. It is these touches throughout Seam that make it a very cohesive framework.

For the projects page, we can use the same HTML from the Spring application.

Example 3.3. JSF page for displaying the projects

					
<h:dataTable value="#{projects}" var="v_project">
	<h:column>
		<f:facet name="header">ID</f:facet>
		<h:outputText value="#{v_project.id}" />
	</h:column>

	<h:column>
		<f:facet name="header">Title</f:facet>
		<s:link value="#{v_project.title}" view="/projectView"
			propagation="none">
			<f:param value="#{v_project.id}" name="projectId" />
		</s:link>
	</h:column>

	<h:column>
		<s:link value="Edit" view="/projectEdit" propagation="none">
			<f:param value="#{v_project.id}" name="projectId" />
		</s:link>
	</h:column>

</h:dataTable>
				

When we use s:link or s:button Seam JSF controls, we are using controls that use GET instead of POST. They also provide us with conversation management attributes which is what the propagate attribute is. Since each page runs in a conversation, even if it is a short lived one, there is a chance that the next page will re-use the same conversation as the last one. By setting propagation to none, we specify that the conversation is not shared between the two pages, and a new one is required for the new page. Also, as this was typed out the code complete features in the page editor and the WYSIWYG editor enabled me to quickly pick my backing bean names.

Figure 3.3. Seam Code Assist

Seam Code Assist

Also note that we are able to use code completion on JSF tags, so we just enter "h:output" it will suggest a number of options. If we select the outputText element, it will add a value attribute to put the text value into. This value attribute also has auto completion, just like almost every other attribute and JSF element . The Exadel and JBoss Tools team have done an exceptional job with these tools.

If we start the server and go to our page, we will see a list of our projects. If you make a slight change to the page and refresh the page, it will take a couple of seconds for the change to be published to the server. It is an annoyance, but it is tempered slightly by the fact that you can do a preview in the IDE so you don't have to launch it in the browser to see every mistake you've made. For some reason, this delay seems longer on my machine than others I have seen.

Figure 3.4. List of projects in Seam

List of projects in Seam

3.4. Editor Pages

You will notice that to list the projects, we haven’t used any kind of flows yet. As we discussed earlier, every page runs in a conversation even if it is one that only lasts from this page to the next. Furthermore, we haven’t declared any DataModels yet. For now, we can use page parameters to pass the projectId over to the edit page. To make this easier, we use some Seam provided JSF controls. The s:link and s:button components provide us with a link and button component that we can use to call pages, and pass parameters easily. These requests are done as GET requests as opposed to a POST that JSF would normally use.

In our JSF page, we define the column for the edit link using a Seam s:link component.

Example 3.4.  Column for the edit link in projects.xhtml

					
<h:column>
	<s:link value="Edit" view="/projectEdit" propagation="none">
		<f:param value="#{v_project.id}" name="projectId" />
	</s:link>
</h:column>
				

When rendered in a page, this becomes /projectEdit.seam?projectId=3 and acts as a GET request into projectEdit.xhtml . The reason we have the propagation="none" attribute is because by default, the s:link and s:button components propagate the conversation to the next page. In this case, we don't really want to. We want the edit page to grab a fresh instance of the project we are about to edit. We use the same attribute in the view link in the project title column.

Now lets look at the view and edit page, starting with the view page. We can just whip one up with a simple panel grid.

Example 3.5. Simple JSF project viewer

					
<h:panelGrid columns="2">
    Project Id : <h:outputText value="#{project.id}" />
	Title : <h:outputText value="#{project.title}" />
</h:panelGrid>
				

Now lets consider the problems we face to generate the variable project . We need to take the parameter, and load the project based on the parameter and push it out as a scoped variable called project .

We'll do this the most direct way for now to give you an idea of how straightforward dealing with Seam components can be.

We'll create a new class called ProjectHomeBean which we will use to handle the parameter and the instance of the project. First we define a local interface for it called ProjectHome .

Example 3.6.  ProjectHome interface for handling project instances.

					
@Local
public interface ProjectHome {

	Project getProject();
	void setProject(Project project);
	Long getProjectId();
	void setProjectId(Long id);
	public void remove();
	String save();
    String cancel();

}
				

Next, we create a class called ProjectHome which implements this interface. If we wanted to make this a javabean POJO, we could omit the interface and the stateless annotation and Seam would use this bean the same. You would however have to add transaction annotations similar to the Spring bean ones.

Example 3.7.  ProjectHome implementation

					
@Name("projectHome")
@Scope(ScopeType.CONVERSATION)
@Stateful 
public class ProjectHomeBean implements ProjectHome {

	@In
	private EntityManager entityManager;
	
	@Logger
	private Logger log;
	
	private Long projectId;
	
	private Project project;
	
	private boolean hasProjectId() {
		return (projectId != null && projectId != 0);
	}

    @Factory("project")	
	public Project getProject() {
		if (project == null) {
			if (hasProjectId()) {
				project = entityManager.find(Project.class, projectId);
			} else {
				project = new Project();
			}
		}
		return project;
	}

	public Long getProjectId() {
		return projectId;
	}

	public String save() {
		entityManager.persist(project);
		entityManager.flush();
		FacesMessages.instance().add(
            "Saved changes to project #{project.title}");
		return "save";
	}

	public void setProject(Project project) {
		this.project = project;
	}

	public void setProjectId(Long id) {
		this.projectId = id;
	}
	
	public String cancel() {		
		entityManager.refresh(project);
		return "cancel";
	}

	@Remove @Destroy
	public void remove() {
	
	}
}
				

Notice that we added a @Factory annotation onto the getProject() method. This annotation marks this method as the one to call anytime we need a variable called project .

When we save the project, we also add a FacesMessage in code. Notice how we are able to use the EL expression #{project.title} in the message. We'll add a message for the cancel method an alternate way.

In order to load the project we need to somehow bind the projectId parameter with the projectId member in this component. We can do this using pages.xml . pages.xml is one (or more) xml files for providing additional meta data about each page to Seam. When you create an application using the Seam-Gen tools or JBDS, Seam creates an instance of this file with the default exception handling information included to which you can add your page information. Alternatively, you can have multiple files that deal with logical sections of your application. The information contained can related to page parameters, conversations and page flows that start or end on a given page, page navigation information for a given page, actions to execute when a certain page is requested, and even whether a login is required for access to certain pages which can be defined using wildcards.

Example 3.8.  Project view page parameter configuration from pages.xml

					
<page view-id="/projectView.xhtml">
        <param value="#{projectHome.projectId}" name="projectId"/>
</page>
				

This tells Seam that every time is renders this page, it sets the value of #{projectHome.projectId} to the value of the projectId parameter. This binding is two way, in that when we move to the next page, we put the value of #{projectHome.projectId} in a parameter called projectId and send it to the next page .

If we load our project view page, we can see the project is loaded correctly. If we click the browser refresh, we can see from the log that the page gets a fresh instance of the project entity which is good. We refresh the page to see the latest instance, not just to be sent the same output. Now let's consider the buttons to navigate to the edit page, or back to the project list.

Example 3.9.  JSF for the back and edit buttons in projectView.xhtml

					
<h:panelGrid columns="2">
	<s:button value="Back" view="/projects.xhtml" />
	<s:button value="Edit" view="/projectEdit.xhtml">
		<f:param name="projectId" value="#{project.id}" />
	</s:button>
</h:panelGrid>
				

Here, we explicitly define the views we are going to with any parameters needed. This is one way of handling navigation. Because conversations are omnipresesnt, we don't have to rely on conversation mechanisms for passing data around, we can go back to page parameters. This is also beneficial because we can easily create loosely coupled pages. The edit page itself is fairly simple and is almost identical to the view page.

Example 3.10.  projectEdit.xhtml page.

					
<h:panelGrid columns="2">

    Project Id : <h:outputText value="#{project.id}" />
    Title : <h:inputText value="#{project.title}" />

</h:panelGrid>
				

The next question to ask is where does the project variable come from this time? The answer is simple, we just re-used the same code we used for the viewer. When the page is loaded and JSF looks for a variable called project , Seam will use the same factory method to create the project using the projectId parameter. We can deploy this page and run it right away without any further coding. This is one of the benefits of Seam variables being defined application wide.

Figure 3.5. Editing a project with Seam

Editing a project with Seam

We can also access this page directly with a url using parameters such as /projectEdit.seam?projectId=2 .

3.5. Handling Navigation

Let's consider the save and cancel buttons and how we act on them, and navigate to our next page based on the button clicked. We define our two buttons with actions calling the methods on the backing beans.

Example 3.11.  The save and cancel buttons in projectEdit.xhtml

					
<h:panelGrid columns="2">
<h:commandButton value="Save" action="#{projectHome.save}"/>
	<h:commandButton value="Cancel" action="#{projectHome.cancel}"/>	
</h:panelGrid>
				

Our save button calls the projectHome.save() method which saves the project. The cancel button calls the cancel method which simply refreshes the value of the project we are editing. These methods return strings for the navigation handlers to process.

Notice that we switched from using s:button to h:commandButton . Remember s:button and s:link perform a GET request without the POST. If we used the seam components we wouldn't post our changes back to the server. Technically, we could use a s:button for the cancel button since we don't care whether the changes are posted back. Notice that we use the immediate="true" attribute for the command button since this bypasses the validation steps which we don't care about since we are cancelling changes.

Normally, in JSF, navigation is handled using the JSF navigation rules. Seam offers us multiple additional ways to handle navigation. The first is in the JSF faces-config.xml which I've not used for a while. Next we have pages.xml which is Seam specific. We can simply add on the navigation rules to our page information in pages.xml

Example 3.12.  Navigation using pages.xml

					
<page view-id="/projectEdit.xhtml">
    <begin-conversation join="true" flush-mode="manual"/>
    <param value="#{projectHome.projectId}" name="projectId"/>
    <navigation>
        <rule if-outcome="save">
            <end-conversation/>
            <redirect view-id="/projectView.xhtml">
                <param name="projectId" value="#{project.id}"/>
            </redirect>
        </rule>

	  <rule if-outcome="cancel">
        	<end-conversation/>
            <redirect view-id="/projectView.xhtml">
                <message>Cancelled Changes to project #{project.title}</message>
                <param name="projectId" value="#{project.id}"/>
            </redirect>
        </rule>
            
    </navigation>     
</page>
				

This page definition does a number of things. When we enter this page, we want to start a conversation. Since we specified the join="true" attribute, then if one already exists we join that existing conversation.

We then have some navigation rules, one for save and the other for cancel which were the two outcomes from the save and cancel methods called from our projectEdit.xhtml page. If the user saves the project, save is returned to the navigation handler so we end the conversation and redirect to the view page passing in the project Id as a parameter. If they cancel the changes, the cancel method is called which returns cancel . Navigationally, the same thing happens except we also pass a message along with it. This message is rendered on the page that we are redirected to. Notice how we were able to include the EL expression #{project.title} into the message to give the name of the project we were editing. The only reason the message is here is to demonstrate the number of different ways we can do simple things with Seam.

If we run this, edit a project, and cancel the changes we get the message appearing once we have cancelled the changes.

Figure 3.6.  Propagating cancelled messages to the users

Propagating cancelled messages to the users

We came back to the view page with a message to display. If we edit the project and save the changes, the message is added from the saveProject() method in our bean.

Figure 3.7. Propagating save messages to the user

Propagating save messages to the user

Before we move on, let's consider one more scenario, the adding of a new project to the list. We do this by navigating to the projectEdit page with no projectId parameter. By default, we create a new project in our project factory method if no parameter is passed in to the bean.

We can add the create button on our projects page.

Example 3.13.  The add project button on the project.xhtml page.

					
<s:button value="New Project" view="/projectEdit.xhtml" propagation="none"/>
				

This was pretty straight forward, and our projectHome component has handled the cases where we want to view, edit or create a new project instance giving us quite a bit of code re-use without any additional code or configuration needed. All we need to do is request the project variable and Seam will provide it for us. One real benefit here is that we can move the factory method from one bean to another and Seam will automatically use that bean instead. We have created a de-coupling between our view and the source from which we obtain data. To change the source of our project entity in spring would require us changing all the spring web flows that called that one method to generate the project.

Figure 3.8. Create new project

Create new project

Figure 3.9. Save new project

Save new project

Figure 3.10. View new project in list

View new project in list

3.6. The Seam Application Framework

This section introduces a couple of generic components which can be used to build typical CRUD querying and entity handling functions. Note that these components make up the Seam Application Framework, which is just a small (and optional) part of the Seam framework itself. These are really just helper classes that make it really easy to build CRUD and query components.

Seam typically blurs the lines on typical application layering. Our Dao layer has become a couple of beans which hold state, and are themselves stateful. You can choose to create a Dao or service layer and inject it into beans that manage state, but it is somewhat unnecessary. Our data is decoupled from our view through the use of EL. Some people might be wondering where the application layers are and having fits at the notion of having a single bean handle view events and data access. There are good reasons why it makes sense to flatten things down since the flatter version handles 95% of situations, and it is simple to refactor out these pieces if you need to re-use them or access them as separate layers. Also, since the flatter version requires almost no code, it raises the question of whether code that is never written can be shareable?

Seam embraces this notion of thicker stateful beans wholly to the extent of providing generic versions of beans called EntityHome and EntityQuery . These components make up the Seam Application Framework and are used to provide simple and easy classes to fetch, create and persist entities and to query for entity instances respectively. First, we’ll take a look at the EntityQuery as we determine how we are going to display the list of issues for a project. To display the list of issues, we add a data table that connects to our list of issues.

Example 3.14. Issues data table

					
<h:dataTable value="#{issues}" var="v_issue">
	<h:column>
		<h:outputText value="#{v_issue.id}" />
	</h:column>

	<h:column>
		<s:link value="#{v_issue.title}" view="/issueView.xhtml">
			<f:param value="#{v_issue.id}" name="issueId" />
		</s:link>
	</h:column>

	<h:column>
		<s:link value="edit" view="/issueEdit.xhtml">
			<f:param value="#{v_issue.id}" name="issueId" />
		</s:link>
	</h:column>
</h:dataTable>
				

Now we just need to determine how we obtain the value of issues one easy way would be to create a factory method in components.xml which is the configuration file used to define Seam components in XML instead of annotations. We can also use this file to specify factory methods which we do here for the value issues .

Example 3.15.  Define issues factory in components.xml

					
<factory name="issues" value="#{project.issues}" scope="conversation"/>
				

When we open the projectView page, since issues is not defined, Seam evaluates the expression #{project.issues} and puts the results into a Seam context variable called issues in the chosen (or default) scope. Note that we don't have any problems regarding Lazy Initialization Exceptions, because Seam gives us two transactions during the request - reponse process. The first transaction spans the application invokation while the second spans the render response phase of the JSF lifecycle. This way, any problems that might occur from a commit happen during the application invokation and not after the response render where you just rendered a message to the user that everything is ok.

Factory methods are a really nice way to let us create ways to access data in a quick and dirty fashion. For smaller result sets, this may be acceptable, but for many cases, we want to use a method that is more controllable, and lets us use pagination on the returned data. For this, we could create a method on a Dao to return the set of results based on the current page, and the page size, or we can use an EntityQuery . The EntityQuery has methods for managing ordering and pagination built-in and can be used by each query.

For now, we will define our entity query in components.xml .

Example 3.16.  Issue query defined in components.xml

					
<fwk:entity-query name="issuesQuery" max-results="10" scope="CONVERSATION">
    <fwk:ejbql>from Issue</fwk:ejbql>
    <fwk:restrictions>
        <value>project.id = #{project.id}</value>
    </fwk:restrictions>
</fwk:entity-query>
				

This query creates a list of issues that belong to the project identified by #{project} by matching the id attribute of the project , again by making use of EL expressions to great effect. If we were to add pagination to our list of issues, we could use the EntityQuery methods for nextExists() and previousExists() , and call the next() and previous() methods on our query. My article on codeless ordered and paginated tables describes how to use the entity query to great effect in producing highly interactive tables.

Since we are driving our list of issues by the value of #{project} we need to make sure this value is valid on each refresh which with the current implementation, it is. However, it does raise a problem with Seam, we define our issues query in terms of the current value of project . As developers, we need to be mindful that anytime we use this query, we need to have a valid value for project . In most cases, it shouldn't be a problem, but for larger applications, I can see some confusion in keeping track of dependencies. Compare this to Spring where the query and the project id is local to the flow as opposed to globally declared. If the project value is not defined, the restriction will not be included in the query and all issues will be returned by the query.

3.7. Issues Viewer and Editor

Now let's consider the edit/view pages for the issues. This time we'll use the EntityHome piece of the Seam framework so you can see how simple it can be to set up entity management using it. This is not required for using Seam, but is simply a generic entity manager that just contains the code that you would typically end up writing anyway. Early examples for Seam do not use these objects, nor did early versions of the Seam-Gen tools. However, this method of creating applications is becoming almost the standard with Seam.

The code is very similar to the code we wrote for the ProjectHome bean class, except that the EntityHome code is already written and ready for us to use in our subclass. First, lets take a look at our page code that will use our entity.

Example 3.17.  issueView.xml code.

					
<h:panelGrid columns="2">
    Issue Id : <h:outputText value="#{issue.id}" />
    Title : <h:outputText value="#{issue.title}" />
    Project : <h:outputText value="#{issue.project.title}" />
    Status : <h:outputText value="#{issue.status.title}" />
    Description : <h:outputText value="#{issue.description}" />
</h:panelGrid>

<h:panelGrid columns="2">
    <s:button value="Back" action="back" />
    <s:button value="Edit" action="edit" />
</h:panelGrid>
				

Now let's consider where the #{issue} value is coming from. Since we are using EntityHome , we can define it in components.xml using XML.

Example 3.18.  Defining issueHome and a factory for issue in components.xml

					
<fwk:entity-home name="issueHome" 
      entity-class="org.issuetracker.model.Issue" 
      scope="conversation" />
   
   <factory name="issue" value="#{issueHome.instance}" scope="conversation"/>
				

This sets up an EntityHome component called issueHome which will handle the persistence for a class of type org.issuetracker.mode.Issue . We also defined a factory method to be called when the issue context variable doesn't exist. We simply get the value of issueHome.instance which will return the instance of the Issue entity we are currently working with. Note that this all uses lazy initialization, so if the issue variable doesn't exist, Seam calls the factory method for it which is #{issueHome.instance} . If issueHome doesn't exist, then it is created as per our definition in components.xml . When the instance() method is called, if the value held by this EntityHome component is null, it attempts to load one if it has an Id set, or create a new one if it doesn't.

Alternatively, we could write this easilt in code using the following

Example 3.19.  Java code implementation of IssueHome .

					
@Name("issueHome")
@Scope(ScopeType.Conversation)
public IssueHome extends EntityHome<Issue> {

  public Long getIssueId() {
      return (Long) getId();
  }
  
  public void setIssueId(Long id) {
      setId(id);
  }
  
  @Factory("issue")
  public Issue getIssue() {
      return getInstance();
  }
}		
				

This few lines of code lets us implement the same thing we did in xml. The benefits here is that the Ide has better auto-completion when you use the code version, you can provide a type for the id so you no longer need to add the converter in pages.xml for assigning parameters, and you can optionally add backing bean methods if you choose to. Note that we also put the factory method here to generate the issue variable. You can also add code to handle security issues in the event that no issue id is supplied and the user doesn't have rights to create an issue. In order to use the java code mechanism, you will need to delete the definition from components.xml since they share the same component name.

The only other piece we need to deal with is passing the issueId parameter to the issueHome component so we know the id of the issue we are dealing with. For this, we will use pages.xml to provide the parameter to the EntityHome .

Example 3.20.  Binding parameter to entity home id property in pages.xml .

					
<page view-id="/issueView.xhtml">
    <param value="#{issueHome.id}" name="issueId" converterId="javax.faces.Long"/>
</page>
				

Figure 3.11. Issue view

Issue view

Let's consider our navigation for the back and edit buttons. This time, lets determine the navigation from the page by using action strings set on the buttons, similar to what we used with Spring. In pages.xml , we can extend the page definition to include the navigation.

Example 3.21.  Defining navigation for issueView.xhtml in pages.xml .

					
<page view-id="/issueView.xhtml">
    <param value="#{issueHome.id}" name="issueId" converterId="javax.faces.Long"/>
    <navigation>
        <rule if-outcome="back">
            <redirect view-id="/projectView.xhtml">
                <param name="projectId" value="#{issue.project.id}"/>
            </redirect>
        </rule>
            
        <rule if-outcome="edit">
            <redirect view-id="/issueEdit.xhtml">
                <param name="issueId" value="#{issue.id}"/>
            </redirect>
        </rule>
            
    </navigation>
</page>
				

Here, we have defined the pages to go to when the back or edit buttons are clicked. Using this technique allows us to decouple the navigation from the view, with the navigation being determined based on the outcome from the view. This is important if we are using workflows since a 'back' button may take us to different places depending on the work flow we are currently in.

Now lets create the edit page. Note that again, we re-use our IssueHome component to handle the Issue object persistence tasks for us. We just need to provide the issueId in the parameter.

For the page itself, we will use something a little different that comes with Seam in order to handle layout this time.

Example 3.22.  issueEdit.xhtml page using the seam decorator layout components

					
<s:decorate template="/layout/edit.xhtml">
	<ui:define name="label">Issue Id : </ui:define>
	<h:outputText value="#{issue.id}" />
</s:decorate>

<s:decorate template="/layout/edit.xhtml">
	<ui:define name="label">Title : </ui:define>
	<h:inputText value="#{issue.title}" required="true" />
</s:decorate>

<s:decorate template="/layout/edit.xhtml">
	<ui:define name="label">Project : </ui:define>
	<h:outputText value="#{issue.project.title}" /> 
	<h:commandLink action="selectProject" value="change" />
</s:decorate>

<s:decorate template="/layout/edit.xhtml">
	<ui:define name="label">Status : </ui:define>
	<h:selectOneMenu value="#{issue.status}">
		<s:selectItems value="#{issueStates}" var="v_status"
			label="#{v_status.title}" />
		<s:convertEntity />
	</h:selectOneMenu>
</s:decorate>

<s:decorate template="/layout/edit.xhtml">
	<ui:define name="label">Description : </ui:define>
	<h:inputTextarea value="#{issue.description}" />
</s:decorate>

<s:div styleClass="buttonSet">
	<h:panelGrid columns="2">
		<h:commandButton value="Save" action="#{issueHome.update}" />
		<h:commandButton value="Cancel" action="#{issueHome.cancelChanges()}"/>
		<h:commandButton value="dirtys" action="dirty" />
	</h:panelGrid>
</s:div>
				

Well this looks a bit different! This uses the s:decorate tag that Seam provides. It lets you use a Facelet template to decorate a JSF tag. It extends the Facelets decorate tag by making the values #{invalid} and #{required} available inside of the template . These values represent whether the input control has a validation error or if it is required. This way we can display the field differently if it is invalid or is required. You can easily write your own edit.xhtml template or use the Seam version. In this case, the Seam version uses CSS to position the form controls. The decorator also automatically wraps the control in Seam validation tags which automatically performs validation using the hibernate validators on the model. This reuse of validation annotations is another strong point for Seam.

Besids the strange layout method, this form is fairly straightforward. However, the one piece we should take special note of is the drop down selection for the issue status values.

Example 3.23.  Status selection using Seam selectItems components.

					
<s:decorate template="/layout/edit.xhtml">
<ui:define name="label">Status : </ui:define>
	<h:selectOneMenu value="#{issue.status.title}">
		<s:selectItems value="#{issueStates}" var="v_status"
label="#{v_status.title}" />
		<s:convertEntity />
	</h:selectOneMenu>
</s:decorate>
				

Seam provides JSF tags to allow you to bind drop down lists or listboxes to entity lists without requiring you to wrap them in JSF select items. It also provides a converter that uses the default entityManager to convert the selection to an entity. This makes lookup lists from entities really simple to implement. In order for this to work, I need to create a list of the issue status values called issueStates . I could use an Entity Query defined in components.xml .

Example 3.24.  EntityQuery for grabbing a list of the issue status objects.

					
<fwk:entity-query name="issueStatusQuery">
       <fwk:ejbql>from IssueStatus</fwk:ejbql>
   </fwk:entity-query>
   
   <factory name="issueStates" value="#{issueStatusQuery.resultList}"/>
				

Instead of "programming in XML", I'm going to create the same class in code by writing a class that extends the EntityQuery the same way we did for the IssueHome bean.

Example 3.25.  Implementation of the issueStatusQuery .

					
@Name("issueStatusQuery")
@Scope(ScopeType.CONVERSATION)
public class IssueStatusQuery extends EntityQuery<IssueStatus> {
	
	@Override
	public String getEjbql() {
		return "from IssueStatus";
	}
	
	@Factory("issueStates")
	public List<IssueStatus> getIssueStatuses() {
		return getResultList();
	}
}
				

It takes only a little more code to define this in Java as opposed to using XML, and we get exactly the same result. Again, the benefit to doing it here is that we can add additional methods on there, as well as type safety, and also better auto completion in the IDE.

One last thing we need to add is to map the issueId parameter to the issueHome.id value for the issueEdit.xhtml page. This is a step I usually forget until I go to the page and am greeted with a blank page.

Example 3.26.  pages.xml parameter and conversation definition for issueEdit.xhtml .

					
<page view-id="/issueEdit.xhtml">
<begin-conversation/>
      <param value="#{issueHome.id}" name="issueId" 
        converterId="javax.faces.Long"/>
</page>
				

Figure 3.12. Issue editor

Issue editor

The drop down is selecting the current value correctly, and as we'll see, it will put any newly selected value back in the model. Let's deal with the save and cancel buttons.

This time, we'll use the string results from the actions to perform navigation. Also, bear in mind we will later be adding the code to select which project this issue belongs to. For this reason, we'll make this process a work flow, or as Seam calls them page flows which is the third and final mechanism we have for handling navigation.

We'll create the pageflow file called issueEdit.jpdl.xml .

Example 3.27.  issueEdit.jpdl.xml page flow for editing the issue.

					
<?xml version="1.0" encoding="UTF-8"?>
<pageflow-definition xmlns="http://jboss.com/products/seam/pageflow"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://jboss.com/products/seam/pageflow 
	  http://jboss.com/products/seam/pageflow-2.0.xsd"
	name="issueEdit">
	<start-page view-id="/issueEdit.xhtml" name="issueEdit">
		<transition name="updated" to="endState"/>
				
		<transition name="cancel" to="endState"/>
	</start-page>
	
	<page name="endState" view-id="/issueView">
		<end-conversation />
	</page>
</pageflow-definition>
				

In order to incorporate this pageflow into the Seam application, we need to add it to components.xml in the place defined in the default version of the file.

Example 3.28.  Adding the pageflow to components.xml

					
<bpm:jbpm>
    <bpm:pageflow-definitions>
        <value>issueEdit.jpdl.xml</value>
    </bpm:pageflow-definitions>
</bpm:jbpm>

				

In pages.xml we alter the begin conversation attribute to include the name of the page flow to use when we start the conversation. The page flow typically operates as part of a conversation, with all data in the page flow being conversational data held in the conversation.

Example 3.29.  Invoking the page flow when the conversation is started.

					
<page view-id="/issueEdit.xhtml">
    <begin-conversation join="true" pageflow="issueEdit" flush-mode="manual"/>
    <param value="#{issueHome.id}" name="issueId" converterId="javax.faces.Long"/>
</page>
				

We also specify that the flush-mode on this conversation is set to manual which means that changes to our entities are not flushed until we manually call the flush method. This is similar to adding the persistence context tag to the Spring flows making the PC last for the duration of the conversation.

We add two buttons in issueEdit.xhtml for saving and cancelling changes.

Example 3.30.  JSF for the save and cancel buttons in issueEdit.xhtml

					
<h:commandButton value="Save" action="#{issueHome.update}" />
<h:commandButton value="Cancel" action="cancel"/>
				

When the user clicks the save button, we call the update method of the issueHome component. This persists our changes and flushes the persistence context. It also adds a faces message indicating the instance has been saved. The update method returns a string of updated which we respond to in the pageflow. In the event of an updated or cancel string action, we navigate to the endState which is the issue view page.

Notice that we don't pass any parameter to the issue view page used for the end state. The reason is because even though we end the conversation, the conversation is re-used in the next page , so all the context variables of the conversation are still available when we get to the issue view page. This means we don't need to re-load the issue to view it since there is already a context variable called issue . Besides the fact that I'm not a fan of this implicit passing of data, it also can be problematic. What if we made changes to the issue that did get posted back to the server as part of the user interaction and the current instance of the issue entity is dirty? We need to refresh it somehow. We can actually view this problem in action by adding a button that does nothing on the edit page. If you edit the issue title, click the button (which posts changes but stays on the page), click cancel, then the issue view page displays the changed issue with the wrong title.

Fixing this problem is simple - kind of. We need to either invalidate both the issue variable and the value of issueHome.instance which is where it will be obtained from when it is not found. Remember, our home bean is stateful so even though the issue variable can be invalidated, it is pointless if Seam is just going to fetch the dirty copy from issueHome.instance via the factory method.

Alternatively, we can just refresh the current instance of the issue we are using. . The other option is to use events. Events can be raised from numerous places, either via code or as part of the page flow or pages.xml navigation. We might raise an event for when we cancel changes to the Issue. We could then outject a null issue value or just set it in code, and invalidate the issue instance held in the issueHome component. However, it seems a little complex to create event listeners for every kind of entity we want to edit.

Unfortunately, the default implementation of EntityHome does not contain a refresh or cancelChanges method in which we can do any of this. We can extend the EntityHome class to include this, you can use your own version instead of the built in version because the framework tags have a class attribute that lets you specify the class to use for the EntityHome .

To do this, we create class called EntityHomeExtended which extends the EntityHome .

Example 3.31.  Code listing for EntityHomeExtended

					
public class EntityHomeExtended<E> extends EntityHome<E> {
	
	public String cancelChanges() {
		getEntityManager().refresh(getInstance());
		return "cancel";
	}	
}
				

We could have expanded on this so it follows the standards set in the persist() and update methods with regards to raising events and putting messages in the FacesMessages . To use our new class, we just change the issueHome definition in components.xml .

Example 3.32.  Framework EntityHome using our extended class.

					
<fwk:entity-home class="org.seamtracker.session.EntityHomeExtended" 
    name="issueHome" 
    entity-class="org.issuetracker.model.Issue" 
    scope="conversation" />
				

Alternatively, if you used the java code version of IssueHome , we just extend it from the ExtendedEntityHome . Finally, we amend our page to call the new method when the user clicks cancel.

Example 3.33.  JSF for the cancel button with our new method

					
<h:commandButton value="Save" action="#{issueHome.update}" />
<h:commandButton value="Cancel" action="#{issueHome.cancelChanges()}"/>
				

Now we can make changes, click cancel, and any changes to the issue will be undone with the entity being refreshed by our new method.

3.8. Selecting Projects

Now let's look at the issue of changing the project for the issue. First, let's add a link to change the project. We put this next to the project name display in the issueEdit.xhtml .

Example 3.34.  Adding link to issueEdit.xhtml to select a project.


					
<h:outputText value="#{issue.project.title}" /> 
<h:commandLink action="selectProject" value="change" />
				

Now we need to consider a few things. Until now, we have not used DataModel in our pages, mainly because we haven't needed to since we have been using parameters for passing data around. In order to select a project, we probably want to use a DataModel to create a clickable list. We also need to have some kind of flag that will let us indicate on the project list page that we want to select a project. Since we have our ProjectListBean , we can use that to hold the flag since the whole bean is stateful. We add some methods to the interface to handle these tasks.

Example 3.35.  Additional interface methods for project selection on the projectList interface.

					
Boolean getDoSelect();
void setDoSelect(Boolean doSelect);
public Project getSelectedProject();
public void setSelectedProject(Project selectedProject);
				

We then implement these methods on the projectListBean .

Example 3.36.  Implementation of project selection code in projectListBean .

					
private Boolean doSelect;

@DataModelSelection
private Project selectedProject;
	
public Boolean getDoSelect() {
	return doSelect;
}

public void setDoSelect(Boolean doSelect) {
	this.doSelect = doSelect;
}

public Project getSelectedProject() {
	return selectedProject;
}

public void setSelectedProject(Project selectedProject) {
	this.selectedProject = selectedProject;
}

//--pre existing code
@Factory("projects")
@DataModel
public List<Project> getProjects() {

				

The doSelect flag is used to determine whether the projects page is displayed in selection mode, or normal browsing mode. As we added a @DataModel annotation to the getProjects method, when Seam fetches the list of projects it wraps the list in a DataModel , and we can have a list of entities which can be used as clickable links. When a DataModel row link is clicked, Seam can automatically push the entity into a property which has been annotated with the @DataModelSelection . By default the single DataModelSelection is populated from the single DataModel clicked item. If you have more than one DataModel on a bean then Seam cannot assume the defaults, and you have to start naming the data models and the associated datamodel selections.

In our pageflow, we add the transition and the page for selecting the project, and the method call to set the project value on the issue entity.

Example 3.37.  Adding the transition for selecting a project for the issue.

<start-page view-id="/issueEdit.xhtml" name="issueEdit">
	<transition name="updated" to="endState" />
	<transition name="cancel" to="endState" />

      <!-- new transition -->
	<transition to="projectState" name="selectProject">
		<action expression="#{projectList.setDoSelect(true)}" />
	</transition>
</start-page>

<page name="projectState" view-id="/projects.xhtml">
	<transition name="cancelSelection" to="issueEdit" />
	<transition name="selected" to="issueEdit">
		<action expression="#{issue.setProject(projectList.selectedProject)}"/>		
	</transition>
</page>

When we click on the select project link, we set the doSelect flag on the projectList bean to true. When we get to the projects page, we use the projectList bean that we created in the issue edit page to display the projects. When displaying the projects, we query the select flag to determine whether the projects can be selected. If so, we display the select link in a column in the data table.

Example 3.38.  Column for selecting a project in the table.

<h:column rendered="#{projectList.doSelect == true}">
	<f:facet name="header">Select</f:facet>
	<s:link value="Select" action="selected"/>
</h:column>

We only render the column if the doSelect flag is set and the link returns an action of selected . Going back to our page flow, in the projectState state, we have a transition for selected that sets the project for the current issue to the selected project value in the projectList bean.

Example 3.39.  Transition to set the project when selected

<transition name="selected" to="issueEdit">
		<action expression="#{issue.setProject(projectList.selectedProject)}"/>		
</transition>

If the action is selected then we first call the setProject() method on the issue, and set it to the current selectedProject on the projectList bean. We then transition to the issueEdit node which is the issueEdit page.

One annoying problem I found with Seam pageflow is that I had a couple of errors in my code which resulted in an endless loop for the navigation handler. Not only is it annoying but it is hard to pull out the real cause of the problem since it gets scrolled off the console display if it is displayed at all.

3.9. The Seam Framwork Approach

The approach the Seam team took to letting you interface with the pieces of the Seam Framework was to instantiate them as Seam components just like the components you write. That means, as the manual says, you get to work with "one kind of stuff". For example, in your web page, you can optionally render a 'debug panel' which includes all sorts of framework related information such as:

Example 3.40. 

#{conversation.id}
#{conversation.isLongRunning}
#{pageFlow.hasDefaultTransition}

These components are also easily available in code by either injecting them, or using the static access method. Just about every piece of the Seam Framework is accessible using these three methods which makes the Framework very accessible.

@In
Private Conversation conversation

Or using :

PageFlow.getInstance();

Since these components are exposed via EL, and Seam tries to work in EL in almost every place it can (pageflows, messages, debug log messages, queries, sql) you can access these components from just about anywhere. Also, because the tooling the IDE team have built gives you plenty of EL code completion, the "one kind of stuff" rule means you also get auto-completion on these framework components.

Additionally, the system components can be replaced with your own versions if you chose to. There is precedence to component installation which lets you override the default implementation with your own. This component precedence is also used to instantiate Mock or test components when running tests instead of the actual components.

3.10. Other Features

Seam comes packed with a bunch of additional features from PDF and email generation using JSF tags to Seam Remoting. It extends the EL expressions used in JSF to allow the use of parameters in calls. It includes JSF components that provide GET request features as opposed to JSF's POST only implementation. It comes with the ability to integrate Spring beans into your Seam application as first class Seam components, as well as the ability to integrate with GWT. Seam also allows you to create conversational web services in which you can hold a stateful conversation with the server from a variety of clients. Seam also allows for the injection of the POJO Cache as a Seam component, and also allows for the caching of rendered JSF content fragments with a single JSF tag. Seam comes bundled with Ajax4Jsf and the Richfaces JSF component set which gets you up and running and creating ajaxified applications in no time.

Seam also comes with Seam-Gen than not only generates the basic application, but can be used to generate forms and JPA entity objects from data tables as you want to add new forms. While this works pretty well, and can speed things up a bit and can get you going, there can be problems with different ideas on how tables should be named and we are getting into code generator territory which means you start losing your grip on how your app is written. However, the basic Seam-Gen function of creating an app that is ready to be deployed works very well and takes a minimalist approach to code generation.

The number of options avilable regarding security is lacking, but as of writing (June 2008) it is undergoing a serious overhaul and from first glances, it looks like it will be almost on par with Spring Security. Like most features of Seam, security is available from EL expressions, and also can be applied at the page level, method level, bean level or HTML component level.

Seam comes with a transaction manager that solves problems relating to committing changes and then needing a transaction when rendering a view. Typically with web frameworks if you use the Open Session In View solution to the lazy initialization exception, you have a transaction that spans from the start of the request to the end of the request when the response has rendered. However, what if you save data in the action, and render a success message only to find that you have an error when you commit the data in the transaction? If you commit the transaction after the bean action, you could get and LIE if you require additional pieces of the object graph to render the response. Seam's answer to this is to use two transactions. The first is used to wrap any persistence that takes place in the bean action. The second is used to provide any additional object loading in the rendering phase of the request. Also, if something fails in the update during the bean action, we find out before we are rendering a response, and we can actually let the user know.

Seam also provides process flow management so you can create work flows that last longer than any user session or conversation as well as incorporating JBoss Rules for defining business rules. However, I have had little experience with either of these two Seam functions.

Pages.xml lets you define pages to default to in the case of exceptions, as well as defining what actions to take on a specific page if there is no active conversation, and whether a conversation is required. This can take care of the issue of the double submit problem since you can't go back and reopen a conversation that closed when you clicked submit the first time round.

Seam really does tie together a number of good functions and features in a single stack which is something I think java has been missing for a while. What's more, it isn't just a collection of distributed libraries, they are pulled in and made a part of the Seam framework. This was one of the goals behind the Seam framework, to make the whole far greater than the sum of it's parts. Yes, in other applications, you can use iText for PDF generation, but only Seam lets you access it through JSF components using the same Facelets and EL data access mechanisms that you have already set up. Seam embraces and provides plenty of value add ons for these libraries. Seam also appears to provide a lot more flexibility than Spring on how you get things done by offering multiple ways to do the same thing. The downside to this is that there is no 'best practices' way of doing things and some ways could be more problematic in the long run and as applications grow. The demos were mostly written before the Seam Application Framework was finalized, and the framework is now the recommended method while their Hotel booking example still focuses on passing objects around between beans which I am not a fan of.

JBoss worked with Exadel on making their JSF editor tools and Richfaces a part of the JBoss offerings, and includes seamless integration with Seam. Exadel have now also come out with Flamingo which lets you use Seam with Adobe Flex.

Seam has sophisticated conversation management using a single parameter to keep track of the conversation for each page. Seam also supports the notions of workspaces whereby you can have multiple conversations running at the same time and choose between them. Each conversation can be given a description which can be used to distinguish between the conversations. For example, if you search for hotels, and decide to open up a booking form in a new window for 4 of them, you create 4 workspaces that you can switch between. Each one has an assigned description such as Book Hotel #{hotel.name} so the workspace description has meaning. You can also use business conversation Ids so if your URL is /projectEdit.seam?projectId=12 you can tell Seam to use the project Id value as the conversation Id for a given page. The advantage here is that if you navigate off that page, and go back to /projectEdit.seam?projectId=12 you will end up back in the same conversation. Seam also lets you assign a timeout value for conversations and if needed, you can assign a different timeout value for individual conversations. Tie this in with RESTful Urls using URL rewriting which now comes with Seam, and you can use /projects/12/edit as your URL and not only will it manage your conversation for you, but it will give you a bookmarkable URL with JSF.

Seam also has a great Events system which lets you trigger actions in backing beans in response to events raised by your application, or events fired by the Seam framework itself. These events work on a subscriber basis so adding another observer to the event is a simple as annotating a Seam component with @Observer([eventName]) . You can also raise your own events and add observers to them so you could have a bean that receives a specific event (i.e. closing a case) to send an email to a supervisor. Data can also be passed in events so the particular case can be passed to the event observer.

The other aspect of Seam is as a conversational backend for non-JSF front ends. I haven't really played with this that much, but there is a Wicket-Seam interface in the works, there have been a number of posts about getting GWT to work with Seam, and Exadel have released Flamingo which is their Seam interface for Adobe Flex. Seam lets you write conversational Web Services which is another way to get more mileage out of the code you write.

Seam Remoting lets you call your server side Seam Components from Javascript with a couple of bean annotations and javascript include files added to the web page. This opens up plenty of possibilities for creating lightweight sites that run mostly on the browser with only a minimal amount of server interaction. Not only can you call business bean methods but also gain access to your domain model from javascript. You can also use conversations in your interactions with the server.


http://www.andygibson.net/