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.jboss.com/products/seam.

ICEfaces v1.6 and v1.7 have been tested with jboss-seam-1.2.1.GA and jboss-seam-2.0.0 GA. In addition, ICEfaces v1.6.1 is included in the jboss-seam-2.0.0.GA distribution package and is used by default with the seam-gen utililty in that package. However, any version of ICEfaces v1.6.1 or greater can be used with jboss-seam-2.0.0.GA.

Note: Seam 1.2.1.GA may use either JSF 1.1 or 1.2 specifications, whereas Seam 2.0.x GA requires the use of JSF 1.2 specifications. The two Seam versions also have different dependencies on external libraries (JARs).
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 icefaces-seam-gen utility which is available as a separate download at http://downloads. icefaces.org/ under the Projects section. The USAGE and README files, which are included in the bundle, describe how to install and run the ICEfaces-specific version of seam-gen from your Seam installation. A tools download for icefaces-seam-gen integration is available for Netbeans-5.5 on the ICEFaces download page.

The seam-gen tool distributed with jboss-seam-2.0.0.GA now includes an ICEfaces option which will generate all the proper configuration with a build script for your project to be deployed on a jboss-4.2.* AS. A basic seam/ICEfaces project is created with the "new-project" target. See other available targets in the README file of the seam-gen distribution.

A hotel booking example enhanced with ICEfaces is also distributed with jboss-seam-2.0.0.GA in the seam-icefaces examples directory.

For jboss-seam-1.2.1.GA, an EAR deployment of ICEfaces Component Showcase implemented as a Seam application is available as a separate download at http://downloads.icefaces.org/. Three targets are included for creating an EAR deployment of seam-comp-showcase:

With jboss-seam-2.0.0.GA, Seam may also be used without an EJB3 container in the application server. A .war deployment of seam-comp-showcase is available for jboss-seam-2.0.0.GA that may be deployed to various servers. See the README file for a listing of servers and how to build and deploy this application. The best results are obtained when JSF specifications match that of the server. In other words, if an application server has JEE specifications, jboss-seam-1.2.1.GA with the myfaces (jsf-1.1 specifications) JARs are best to use. ICEfaces 1.6.0 and subsequent versions have all been tested with this configuration. If an application server is denoted J5EE, then jboss-seam-2.0.0.GA, jsf-1.2 specifications will provide the best solution. The .war deployment of seam-comp-showcase requires a minimum version of ICEfaces-1.6.2 and works best on J5EE AS.

Configuring a Seam ICEfaces Application for jboss-seam-1.2.1.GA

To avoid classloading issues between Seam and ICEfaces, you must use the following approach:

1. Use an EAR type of deployment. Examples of how to create an EAR are available in the Seam documentation, or you can use seam-gen and specify ear at the appropriate prompt.
2. Package the ICEfaces JARs in the EAR directory and specify them as modules in the application.xml. The following JARs need to be included:

The EAR contains a reference to the <application>.war (or web tier) package and a reference to the <application>.JAR (or EJB3) package. Ensure that the ICEfaces JARs are not included in the .war file's WEB-INF/lib directory, as this will override the JARs being loaded from the EAR's application.xml.

The correct web.xml is dependent on which version of the JSF specification is used. This file is included in the web tier.


JSF 1.1 specifications -for jboss-seam-1.2.1.GA (recommended) and earlier distributions of Seam
Note: Modules must be specified in applications.xml and only EAR deployments with ICEfaces is supported.

Although .war deployment is possible, there is no example application showing this. The myfaces*.jars are included in the jboss-seam-1.2.1.GA distribution. To see the configuration and packaging, download the seam-comp-showcase EAR example or icefaces-seam-gen from the ICEfaces download site.

JSF 1.2 specifications - for jboss-seam-1.2.1.GA or jboss-seam-2.0.0.GA

For JSF 1.2 specifications (using Sun JSF 1.2 RI jars), the modules no longer need to be defined as such, but the JARs can just be included in the META-INF\lib directory (other than jboss-seam.jar). See the examples or generate an application using icefaces-seam-gen (jboss-seam-1.2.1.GA) or seam-gen(jboss-seam-2.0.0.GA).

Only one version of faces-config.xml is needed. Ensure that no other facelet view handler is used. The following is an example of faces-config.xml for jboss-seam-1.2.1.GA. The same file for jboss-seam-2.0.0.GA does not require the phase-listener.

<?xml version='1.0' encoding='UTF-8'?>
 
<!DOCTYPE faces-config PUBLIC
 
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
 
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
 
<faces-config> 
 
  <application> 
 
   <message-bundle>messages</message-bundle> 
 
      <view-handler> 
 
          com.icesoft.faces.facelets.D2DSeamFaceletViewHandler 
 
      </view-handler> 
 
   </application>
 
   <!-- Seam transaction management --> 
 
   <lifecycle> 
 
      <phase-listener> 
 
           org.jboss.seam.jsf.TransactionalSeamPhaseListener
 
      </phase-listener> 
 
   </lifecycle> 
 
</faces-config>
 
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 a working example of TimerBean that uses the RenderManager to set up an interval renderer to do clock ticks, generate a seam-gen project (either jboss-seam-1.2.1.GA and icefaces-seam-gen) or jboss-seam-2.0.0.GA and its distributed seam-gen application.

Refer to the notes that follow for more information on the marked sections of the code example.

@Name("timer")
 
@Scope(ScopeType.PAGE)
 
public class TimerBeanImpl implements Renderable, TimerBean {   [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. However, when running in a Seam application, the InputBean is a Seam component and InputBeanImpl is wrapped by Seam interceptors. This interception of an ad hoc method call from an external bean will fail on return as Seam attempts to clean up contexts that it has not set up. The work-around for this is to turn off Seam interception in the bean declaration, as illustrated in the InputBeanImpl class in the FileUpload example found in the Seam variant of the component showcase demo which is available as a separate download.

Note: The side-effect of turning off this Seam interception is that all Seam features depending on interception (bijection, transactions, annotation support) are disabled for the Bean for which interception is turned off.

For the Seam-1.2.1.GA version of the ICEfaces Component Showcase sample application, we use an inner class in the InputBeanImpl class in the FileUpload example to get around a concurrent access problem when you make the BackingBean generate a render from inside the Bean itself. Concurrent method calls to the same object are not allowed in an EJB container. See the example in the FileUploadServlet. The .war deployment example of jboss-seam-2.0.0.GA looks similar to a regular component-showcase example of fileUpload and takes advantage of the PersistentFacesState in order to update the progressMonitor of the fileUpload component. An IntervalRenderer is used for the example of progressMonitor in this same application.



Copyright 2005-2008. ICEsoft Technologies, Inc.
http://www.icesoft.com

TOC PREV NEXT INDEX