JSF 2

  • Tags : ria, jsf
  • Latest : 0.1
  • Last Updated: 01 April 2010
  • Grails version : 1.2.0 > *
  • Authors : null
5 votes
Dependency :
compile ":jsf2:0.1"

Documentation

Summary

Installation

grails install-plugin jsf2

When jsf2-plugin is starting, a copy of the generated web.xml is done into /web-app/web-inf/web.xml. This is due to a tomcat plugin bug when fetching webXmlLocation property. Do a backup of your file or include it in your src/template/web.xml.

Description

Following JSF-ICEfaces plugin...

Based on icefaces plugin work. It prepares the background for ICEfaces 2 plugin but I preferred to split implementation from standard in order to allow developper to make their own JSF framework integration.

JSF-2 integration

  • Bean By convention (no need for faces-config)
  • Bean access to controller parameters (session,request,params...)
  • Beans dynamic methods for navigation redirect(action?,bean?,view?,uri?,url?) and render(action?,view?,bean?),
  • Automatic bean and service properties resolution
  • 'void init()' called at bean initialization if present
  • 'void dispose()' called at bean destruction if present
  • JSF Extended request scope ('view')
  • Access to web.xml configuration via Jsf2Config.groovy
  • Access to faces-config generated in web-app/faces-config.xml
  • Converters for Locale, Closure, Currency, Timezone
  • i18n ready, fast access with #{m['key']}
  • create-bean script
  • Hibernate session managed from view rendering to view response
  • Execute groovy code in EL expression ( #{gtag.groov[' def i = 2; 3.times{ i++ }; i; ']} )
  • Support JSF2 Components - @ManagedBean ...

Extra methods for beans :

  • Support tagLibraries methods
  • Support controllers properties
  • redirect(action?,bean?,view?,uri?,url?),
  • render(action?,view?,bean?)
  • facesMessage(summary?, details?, severity?)

Sample code

Use of create-bean package.Class will do this :

package app

class DummyBean{

def static scope = 'view'

//['view' (jsf2special request scope),'request','session','globalSession' (portlet),'conversation'(swf),'flow'(swf)]

def property = "hi" def oldProperty = "bye"

//def otherBean (autowired bean)

void init(){ //called when bean starts }

void dispose(){ //called when bean stop }

/*String action() { }*/

void say(javax.faces.event.ActionEvent ae) { // render "test2.xhtml" change view // redirect "test.xhtml" redirect view, forcing page scoped bean clean }

void change(javax.faces.event.ActionEvent ae) { def changed = property property = oldProperty oldProperty = changed } }

Here the text.xhtml page (called via configured extension, by default .xhtml)

<?xml version='1.0' encoding='utf-8'?>
<f:view xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
    <html>
    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>        
    </h:head>
    <h:body>
    <h:outputScript name="jsf.js" library="javax.faces" target="body" />
    <h:form>
        <h:panelGroup id="prop">
          <h:outputText value="#{dummyBean.property}"/>
          <h:commandButton value="say #{dummyBean.property}" actionListener="#{dummyBean.say}"/>       
          <h:commandButton value="prepare  #{dummyBean.oldProperty}" actionListener="#{dummyBean.change}">
            <f:ajax render="prop"/>
           </h:commandButton>
         </h:panelGroup>
    </h:form>
    </h:body>
    </html>
</f:view>

Navigation by convention

Here some equivalents methods

<h:commandLink action="/bean/test.iface" />
<h:commandLink action="/bean/test" />
<h:commandLink action="test" />
<h:commandLink action="test.xhtml" />

From bean you can use

render action:"/bean/test.xhtml" //and the equivalents seen before
render "/bean/test.xhtml" // == render action:*
render view:"test" // == /bean/test.xhtml
render bean:"bean2" // /bean2/index.xhtml
render bean:"bean2",view:"test" :// /bean2/test.xhtml

Sample redirect convention

redirect url:"www.google.fr"
redirect action:"test"
render action:"test?faces-redirect=true"
<h:commandLink action="test?faces-redirect=true" />

Greets

Success story: using jsf plugin with PrimeFaces and Grails 1.3.7

Update dependencies

These informations are for the plugin version 0.1!

You have to update the dependencies of the plugin to get your project running:

dependencies {
    provided 'org.hibernate:hibernate-core:3.3.1.GA',
			 'javax.servlet:servlet-api:2.5',
			 'com.sun.faces:jsf-api:2.1.2',
			 'com.sun.faces:jsf-impl:2.1.2'

compile 'javax.servlet:jstl:1.2' }

With this settings you use the current Mojarra JSF implementation as a basis.

Patches

These informations are for the plugin version 0.1!

To use the plugin in the grails default environment you have to patch some code.

GrailsResourceResolver

There is a small bug using this plugin and install a deployable WAR. The implementation uses the default GroovyPageResourceLoader which is not available creating and deploy the WAR file.

Try this fix in the constructor of GrailsResourceResolver of the plugin:

public GrailsResourceResolver() {
		System.out.println("GrailsResourceResolver: constructor");

// Bugfix: ResourceResolver only during Development, SB, 2011-11-24 if (ApplicationHolder.getApplication().getMainContext() .containsBean(GroovyPageResourceLoader.BEAN_ID)) { System.out.println("GrailsResourceResolver: get groovyPageResourceLoader");

this.resourceLoader = (GroovyPageResourceLoader) ApplicationHolder .getApplication().getMainContext() .getBean(GroovyPageResourceLoader.BEAN_ID);

} else if (ApplicationHolder.getApplication().getMainContext() .containsBean("groovyPageResourceLoaderJSF")) { System.out.println("GrailsResourceResolver: get groovyPageResourceLoaderJSF");

this.resourceLoader = (GroovyPageResourceLoader) ApplicationHolder .getApplication().getMainContext() .getBean("groovyPageResourceLoaderJSF"); } }

After this create a new bean instance of the page resolver in the applicationContext.xml:

<bean id="groovyPageResourceLoaderJSF"
		class="org.codehaus.groovy.grails.web.pages.GroovyPageResourceLoader">
		<property name="baseResource">
			<value>/WEB-INF/grails-app/views</value>
		</property>
	</bean>

Debug resource resolver

If you have problems receiving resources on your JSF pages like images or stylesheets try to debug the GrailsResourceResolver:

/**
	 * Bugfix: Get Resource through resourceLoader OR servletContextLoader
	 * 
	 * @param uri
	 * @return
	 */
	private Resource getResourceWithinContext(String uri) {
		System.out.println("GrailsResourceResolver: getResourceWithinContext: " + uri);

if (resourceLoader != null) { System.out.println("GrailsResourceResolver: GroovyPageResourceLoader is active");

if (Environment.getCurrent().isReloadEnabled() && Metadata.getCurrent().isWarDeployed()) {

return resourceLoader.getResource(uri); }

Resource r = servletContextLoader.getResource(uri);

if (r.exists()) return r;

return resourceLoader.getResource(uri); } else { System.out.println("GrailsResourceResolver: trying to get resource through context");

Resource r = servletContextLoader.getResource(uri);

if (r.exists()) { return r; } }

System.out.println("GrailsResourceResolver: trying Spring MainContext");

Resource r = ApplicationHolder.getApplication().getMainContext().getResource(uri);

if (r.exists()) { return r; }

System.out.println("GrailsResourceResolver: trying fixed base path with Spring MainContext");

r = ApplicationHolder.getApplication().getMainContext().getResource("/WEB-INF/grails-app/views" + uri);

if (r.exists()) { return r; }

System.out.println("GrailsResourceResolver: couldn't get resource");

throw new IllegalStateException( "ResourceResolver not initialised correctly, no [resourceLoader] specified!"); }

Using message bundles

JSF was not able to resolve the grails bundles.

Use this workaround:

Place the bundles in the src/java folder of your project.

e.g.

<grails-project>/src/java/i18n/message_en_GB.properties

Configure faces-config.xml for this bundle location:

<application>
        …
	<locale-config>
		<default-locale>en_GB</default-locale>
	</locale-config>
	<resource-bundle>
		<base-name>i18n.messages</base-name>
		<var>msg</var>
	</resource-bundle>
        …
    </application>

Now you can access the bundle in the JSF pages via msg.

<h:outputText value="#{msg['site.title']}" />

Using PrimeFaces

Add the dependecy to your grails configuration:

e.g.

compile 'org.primefaces:primefaces:3.0.1'

After the small fixes and this configuraton you can create JSF2 pages.

Pages are placed under grails-app/views. Beans are placed under grails-app/beans.

Tips and Tricks

Default Index

To set the default start to a JSF page I used a simple grails controller with a redirect:

class HomeController {
	def index = {
		redirect(uri:"/login.xhtml")
	}
}

The config in UrlMappings.groovy:

"/"(controller: 'home', action: 'index')