TOC PREV NEXT INDEX

JBoss Seam Integration


JBoss Seam is a middleware technology that integrates JSF with EJB3. It can vastly reduce the amount of XML configuration required to develop applications. For more information on Seam, see http://www.seamframework.org/.

For jboss-seam-2.x, the latest version of ICEfaces is always used for the ICEfaces example within the Seam distribution bundle. Seam-gen also defaults to this version, which is stored on the jboss-seam repository along with Maven2 Project Object Models (POMs) for the ICEfaces JARs. With seam-gen you may also specify a particular ICEfaces version you have downloaded (refer to the seam-gen help).

Resources

The following additional resources are available if you are developing Seam applications with ICEfaces:

Getting Started

The simplest way to produce a working ICEfaces/Seam application is to use the seam-gen utility that is distributed with any jboss-seam-2.x distribution bundle. A basic seam/ICEfaces project is created with the "new-project" target. Refer to the documentation for the other available targets for seam-gen. A hotel booking example, enhanced with ICEfaces, is also distributed with the jboss-seam distribution packages in the examples folder. This example demonstrates the conversational context and the ability to switch between conversations using a Seam/ICEfaces integration in an EAR deployment.

A seam-comp-showcase is also available for download at http://downloads.icefaces.org/. Several versions are available depending on which ICEfaces version you are using. Both ICEfaces and Seam are rapidly evolving, so newer versions of the application will be made available to take advantage of what they have to offer. Several Ant targets that create a WAR deployment for various application servers for any of the versions for 1.7.0 and newer, including targets for portlets, are available as well. Refer to the readme files in these packages for instructions.

JSF 1.2 specifications - for jboss-seam-2.0.x or 2.1x

For JSF 1.2 specifications (using Sun JSF 1.2 RI JARs), the modules (JARs) of an EAR deployment no longer need to be defined and the JARs can just be included in the META-INF\lib directory (other than jboss-seam.jar which goes in the root of an EAR). See the examples or generate an application using seam-gen (jboss-seam-2.0.x). Only one version of faces-config.xml is required. Ensure that no other facelet view handler other than D2DFaceletViewHandler is used, unless you are using
jsf-delegation.

Bypassing Seam Interceptors

For performance reasons, be sure to use the annotation @BypassInterceptors on any methods which access a property for an Ajax-enabled component. For example, the read only property for an <ice:inputText> component would have this annotation above the getReadOnly() and the setter. This disables much of the overhead of Seam, including transaction management.

Using Server-initiated Rendering
Asynchronous Configuration

To use the RenderManager you must configure the application for Asynchronous execution mode. The web.xml should contain the following configuration.

<context-param>
 
		<param-name>com.icesoft.faces.synchronousUpdate</param-name>
 
		<param-value>false</param-value>
 
</context-param>
 
Note: You should use Synchronous update mode (synchronousUpdate=true) if your application does NOT use the ICEfaces server-initiated rendering feature.

Prior to ICEfaces v1.6, ICEfaces application developers could use JSF to instantiate the application scope RenderManager and cause this instance to be initialized via a property setter on their state bean for initiating an IntervalRenderer, or some similar mechanism for triggering a render portion of the JSF lifecycle. This still works, but as of v1.6, developers can now configure Seam to load the RenderManager as a named component, and can use injection to pass the RenderManager instance to their beans.

Render Manager as Named Component

To get the RenderManager to be loaded as a named Seam component, add the following line to the components.xml file:

<component scope="APPLICATION" auto-create="true" name="renderManager"
 
        class="com.icesoft.faces.async.render.RenderManager" />
 

This is effectively the same as using JSF to create a managed bean instance. There are a couple of ways to gain access to the RenderManager via injection:

1. Declare the RenderManager with the @In Seam annotation.
		@In
 
		private RenderManager renderManager;
 
2. Declare the setter property with the @In annotation.
		@In
 
		public void setRenderManager(RenderManager x) {
 
				renderManager = x;
 
		}
 
Using RenderManager

For an example of how to use OnDemandRenderer, a simple version of seam-auctionMonitor is available for download at http://downloads.icefaces.org. Review the readme file for instructions on how to build and deploy. A simpler API for Ajax Push is available in ICEfaces v1.8. Refer to SessionRenderer .

Using OnDemandRenderer or IntervalRenderer requires you to implement Renderable in your backing bean. The SessionRenderer requires none of this and only requires a few lines of code.

The following is an example of how to use IntervalRenderer with Seam and ICEfaces. To use this, put an el reference on your facelets page to this bean and the properties #{timer.currentTime} to see the clock running:

@Name("timer")
 
@Scope(ScopeType.PAGE)
 
public class TimerBeanImpl implements Renderable              [See NOTE 1 below.]
 
private DateFormat dateFormatter;
 
@In
 
private RenderManager renderManager;
 
private boolean doneSetup;
 
private IntervalRenderer ir;
 
private PersistentFacesState state = PersistentFacesState.getInstance();
 
private String synchronous;
 
private int myId;
 
private static int id;
 

 
public PersistentFacesState getState() {
 
return state;
 
}
 
public void renderingException( RenderingException re) {
 
		if (log.isTraceEnabled()) {
 
			log.trace("*** View obsoleted: " + myId);
 
		}
 
		cleanup();
 
}
 

 
public TimerBeanImpl() {
 
	dateFormatter = DateFormat.getDateTimeInstance();
 
	myId = ++id;
 
}
 
/**
 
* This getter is bound to an <ice:outputText> element
 
*/ public String getCurrentTime() {
 
    state = PersistentFacesState.getInstance();                 [See NOTE 2 below.]
 
    if (!doneSetup) {                                           [See NOTE 3 below.]
 
    FacesContext fc = FacesContext.getCurrentInstance();
 
    synchronous = (String) fc.getExternalContext()
 
			.getInitParameterMap().get(
 
			"com.icesoft.faces.synchronousUpdate");
 
    boolean timed = Boolean.valueOf((String) fc.getExternalContext()
 
			.getInitParameterMap().get(
 
			"org.icesoft.examples.serverClock"));
 
    if (timed) {
 
	ir = renderManager.getIntervalRenderer("org.icesoft.clock.clockRenderer");
 
	ir.setInterval(2000);
 
	ir.add(this);
 
	ir.requestRender();
 
      }
 
   }
 
     doneSetup = true;
 
     return dateFormatter.format( new Date( System.currentTimeMillis() ) );
 
}
 
public String getRenderMode() {
 
     return synchronous + " " + myId;
 
}
 
public String getCurrentConversation() {
 
     Manager m = Manager.instance();
 
       return m.getCurrentConversationId();
 
}
 

 
public String getLongRunning() {
 
	Manager m = Manager.instance();
 
	return Boolean.toString(m.isLongRunningConversation());
 
}
 

 
@Remove
 
@Destroy
 
public void remove() {                                          [See NOTE 4 below.]
 
	if (log.isTraceEnabled()) {
 
		log.trace("*** View removed: " + myId);
 
	}
 
	cleanup();
 
}
 
	public void viewCreated() {
 
	}
 

 
	public void viewDisposed() {
 
		if (log.isTraceEnabled()) {
 
			log.trace("*** View disposed: " + myId);
 
		}
 
		cleanup();
 
	}
 

 
	private void cleanup() {
 
		if (ir != null) {
 
			ir.remove(this);
 
			if (ir.isEmpty()) {
 
				if (log.isTraceEnabled()) {
 
					log.trace("*** IntervalRenderer Stopped ");
 
				}
 
				ir.requestStop();
 
			}
 
		}
 
	}
 
}
 

NOTES:

[1] It is important that the scope of the bean in this case matches the intended behavior. Anytime the bean is not found in a Seam context, it will be recreated, causing a new IntervalRenderer to be launched each time, which is not the desired behavior. So, even though this bean doesn't contain any information that cannot be obtained in the EVENT scope, it must be stored in Page (or a really long running Conversation) scope to work as intended. Choosing the appropriate Scope is an important concept in a variety of situations, such as dynamic menu generation. For example, if the backing bean for a dynamic menu was in conversation scope and was created each time a request was handled, this would cause the menu component to have no defined parents during subsequent render passes because the menu component hierarchy returned is not the same one in the rendered view.

[2] The state member variable must be updated inside one of the property methods that is called when the Bean is used in a Render pass. This allows the IntervalRenderer to update its ThreadLocal copy of the state so that it will always be rendering into the correct view. It doesn't matter which getter is used to update the state member variable, since all the getters will be called with the new PersistentFacesState.

[3] It is important to initialize an IntervalRenderer from the application only once.

[4] On Destroy, be sure to clean up the IntervalRenderer if necessary.

In general, as an injected Seam component, the RenderManager reference is only valid during the execution of methods that are intercepted by Seam. Anonymous inner threads do not fall into this category, even if they call methods on the enclosing bean.

Using the File Upload (ice:inputFile) Component

The ICEfaces FileUploadServlet is anomalous in that it does not initiate a JSF lifecycle while it is processing the file upload. While it is processing, the FileUploadServlet is calling the progress method on an object implementing the InputBean interface, to keep the upload progress bar current. In a non-Seam JSF application, these relationships are managed by JSF.

When running in a Seam application, for each push that indicates progress, the Seam interceptors are bypassed, so injected values are not available. If you use injected objects, it is important that you save attributes in a constructor to ensure they are available for each Ajax Push of the progress monitor. Each version of ICEfaces has seen improvement for this component, so make sure you review the latest seam-comp-showcase for the version of ICEfaces that you are using.

Seam and Component-binding

It is important to note that component-binding must be done differently using Seam because the regular JSF lifecycle has the binding evaluated before the Seam contexts are available. Refer to the seam-comp-showcase for the component showing dataPaginator for an example of how to do component binding with Seam and ICEfaces.



Copyright 2005-2009. ICEsoft Technologies, Inc.
TOC PREV NEXT INDEX