Wednesday, May 30, 2012

Tapestry - Dependency Injection

Before we understand Dependency Injection in Tapestry, let us see some fundamentals.

Tapestry has the notion of Pages, Components and Services.

While all of them are POJOs, Tapestry assumes that the POJOs in /pages folder are Pages, those in /components are Components and /services are Services.

Pages and Components have their corresponding .tml files. A page can be rendered, but a component cannot be rendered. A component can be used by a page.

Services do not have .tml files. Instead, each Service has an explicit interface and implementation. Services provided functionality that other classes can use. For example, a service can implement a business computation, it can transform, filter, process data, do security checks, write logs, handle email communication, schedule events for data backup, etc. Essentially, anything that is not directly related to rendering a page or handling a page event (user clicking on a page) can be made a service.

Tapestry allows Pages, Components and Dependencies to all be injected into each other, using the following annotations:
@InjectPage
@InjectComponent
@InjectService

Additionally, it has the generic @Inject annotation which "automagically" picks up the correct thing to inject (except when it runs into ambiguity - but let us not worry about that now).

In this post, let us create a Service POJO and inject it.

In /src/main/java/org/confucius, create a folder 'services'

In /src/main/java/org/confucius/services, create a Interface GreetDAO.java, like this:

 package org.confucius.services;  
   
 import java.util.List;  
   
 import org.confucius.Greet;  
   
 public interface GreetDAO {  
      public List<Greet> getInternationalGreetsList();  
 }  
   

In /src/main/java/org/confucius/services, create a class GreetDAOImpl.java, like this:

 package org.confucius.services;  
   
 import java.util.ArrayList;  
 import java.util.List;  
   
 import org.confucius.Greet;  
   
 public class GreetDAOImpl implements GreetDAO {  
   
      public List<Greet> getInternationalGreetsList() {  
           List<Greet> greetList = new ArrayList<Greet>();  
             
           Greet englishGreet = new Greet("English", "Hello World!");  
           greetList.add(englishGreet);  
             
           Greet frenchGreet = new Greet("French", "Bonjour tout le Monde!");  
           greetList.add(frenchGreet);  
             
           Greet italianGreet = new Greet("Italian", "Buongiorno a Tutti!");  
           greetList.add(italianGreet);  
             
           Greet spanishGreet = new Greet("Spanish", "Hola Mundo!");  
           greetList.add(spanishGreet);  
             
           Greet swahiliGreet = new Greet("Swahili", "Jambo!");  
           greetList.add(swahiliGreet);  
             
           return greetList;  
      }  
   
 }  
   


Now we need to tell Tapestry to load GreetDAOImpl as an implementation of service GreetDAO.

For this we need to make a Module.

A Module is a place where services are defined, besides other things. Conveniently, a Module is just another POJO - one with a very specific name by convention: AppModule.java.

In /src/main/java/org/confucius/services, create a class AppModule.java, like this:

 package org.confucius.services;  
   
 import org.apache.tapestry5.ioc.ServiceBinder;  
   
 public class AppModule {  
        
      public static void bind(ServiceBinder binder){  
         binder.bind(GreetDAO.class, GreetDAOImpl.class);  
       }  
   
 }  
   


Note that there are other ways to define modules, but we will not worry about that right now. We will just use the default AppModule.java


Finally, we need to make one more change for this Module to get loaded.

By convention, we should name our Tapestry Filter in web.xml 'app'. So update your web.xml, like this:

 <!DOCTYPE web-app PUBLIC  
  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"  
  "http://java.sun.com/dtd/web-app_2_3.dtd" >  
   
 <web-app>  
   <context-param>  
     <param-name>tapestry.app-package</param-name>  
     <param-value>org.confucius</param-value>  
   </context-param>  
   
   <filter>  
     <filter-name>app</filter-name>  
     <filter-class>org.apache.tapestry5.TapestryFilter</filter-class>  
   </filter>  
   
   <filter-mapping>  
     <filter-name>app</filter-name>  
     <url-pattern>/*</url-pattern>  
   </filter-mapping>  
     
 </web-app>  
   


We are all set to inject our new Service.


Update your InternationalGreets.java class to use this GreetDAO service, like this:

 package org.confucius.pages;  
   
 import java.util.List;  
   
 import org.apache.tapestry5.annotations.Property;  
 import org.apache.tapestry5.ioc.annotations.Inject;  
 import org.confucius.Greet;  
 import org.confucius.services.GreetDAO;  
   
 public class InternationalGreets {  
   
   @Property  
   private Greet greet;  
   
      @Inject  
      private GreetDAO greetDAO;  
        
      public List<Greet> getInternationalGreetsList(){  
           return greetDAO.getInternationalGreetsList();  
      }  
 }  
   


If you now rebuild and redeploy HelloWorldTapestry, you will see the International Greets in a table, just like you did before.

However, this time Tapestry injected the GreetDAO service to make the greets available.

Tuesday, May 29, 2012

Tapestry - Grid

Tapestry uses the Grid component to display data tables.

Let us see this by displaying the International Greets in a Grid.

In /src/main/java/org/confucius, create a class Greet.java, like this:

 package org.confucius;  
   
 public class Greet {  
      private String language;  
      private String greeting;  
        
      public Greet(String language, String greeting) {  
           this.language = language;  
           this.greeting = greeting;  
      }  
   
      public String getLanguage() {  
           return language;  
      }  
        
      public void setLanguage(String language) {  
           this.language = language;  
      }  
        
      public String getGreeting() {  
           return greeting;  
      }  
        
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
 }  
   

Update InternationalGreets.java with a method to return a List of Greets, like this:

 package org.confucius.pages;  
   
 import java.util.ArrayList;  
 import java.util.List;  
   
 import org.apache.tapestry5.annotations.Property;  
 import org.confucius.Greet;  
   
 public class InternationalGreets {  
   
   @Property  
   private Greet greet;  
   
      public List<Greet> getInternationalGreetsList(){  
           List<Greet> greetList = new ArrayList<Greet>();  
             
           Greet englishGreet = new Greet("English", "Hello World!");  
           greetList.add(englishGreet);  
             
           Greet frenchGreet = new Greet("French", "Bonjour tout le Monde!");  
           greetList.add(frenchGreet);  
             
           Greet italianGreet = new Greet("Italian", "Buongiorno a Tutti!");  
           greetList.add(italianGreet);  
             
           Greet spanishGreet = new Greet("Spanish", "Hola Mundo!");  
           greetList.add(spanishGreet);  
             
           Greet swahiliGreet = new Greet("Swahili", "Jambo!");  
           greetList.add(swahiliGreet);  
             
           return greetList;  
      }  
 }  
   

Update InternationaGreets.tml to use the Grid component to display the greets, like this:

 <html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
      <body>  
     <t:grid source="internationalGreetsList" row="greet"/>  
      </body>  
 </html>  
   

Since we are using the default Grid, i.e. there are no specific instructions, Grid will show each property of the Greet object in the order in which it appears in Greet.java. Each column will be sortable by default.

If you rebuild and redeploy HelloWorldTapestry, you will see the International Greets in a sortable table.

Tapestry - Navigation

Let us see how to navigate from one page to another in Tapestry.

Let us create two new pages.

In your /src/main/resources/org/confucius/pages, create a file called InternationalGreets.tml, like this:

 <html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
      <body>  
         <p>   
       English: Hello World!   
       <br/>   
       French: Bonjour tout le Monde!   
       <br/>   
       Italian: Buongiorno a Tutti!   
       <br/>   
       Spanish: Hola Mundo!   
       <br/>   
       Swahili: Jambo!   
         </p>   
      </body>  
 </html>  
   

In your /src/main/java/org/confucius/pages, create a file called InternationalGreets.java, like this:

 package org.confucius.pages;  
   
 public class InternationalGreets {  
   
 }  
   


In your /src/main/resources/org/confucius/pages, create a file called AmericanGreets.tml, like this:

 <html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
      <body>  
         <p>   
          Regular: Hi!   
       <br/>   
       Friendly: Whatsup!   
       <br/>   
       Extra Friendly: Hey, How are you doing!   
         </p>   
      </body>  
 </html>  
   

In your /src/main/java/org/confucius/pages, create a file called AmericanGreets.java, like this:

 package org.confucius.pages;  
   
 public class AmericanGreets {  
   
 }  
   


Update Index.tml to contain two new buttons, like this:

 <html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
      <body>  
           <h2>${greeting}</h2>  
             
           <form t:id="greetForm">  
                <input t:id="internationalGreetsButton" t:type="submit" value="Show International Greets"/>  
                <input t:id="americanGreetsButton" t:type="submit" value="Show American Greets"/>  
           </form>  
      </body>  
 </html>  
   


Update Index.java to handle the two new buttons, like this:

 package org.confucius.pages;  
   
 import org.apache.tapestry5.annotations.Component;  
 import org.apache.tapestry5.corelib.components.Form;  
   
 public class Index {  
      private String greeting;  
      private Object formEventReturn;  
        
      @Component(id = "greetForm")  
      private Form form;       
        
      public Index() {  
           this.greeting = "Hello World, Tapestry!";  
      }  
   
      void onSelectedFromAmericanGreetsButton() {   
           formEventReturn = AmericanGreets.class;  
      }  
        
      void onSelectedFromInternationalGreetsButton() {   
           formEventReturn = InternationalGreets.class;  
      }  
   
      Object onSuccessFromGreetForm() {  
           return formEventReturn;  
      }  
        
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
   
      public String getGreeting() {  
           return greeting;  
      }  
        
 }  
   


Note that we have introduced three new methods, two which handle the button clicks, and one which handles the form submission.

We return the .class corresponding to the appropriate page - this tells Tapestry which page to navigate to.

If you rebuild and redeploy HelloWorldTapestry, you will see two buttons which navigate you to International and American greets.

Tapestry - Bean-side Components

Tapestry allows you to specify components in the bean, instead of in the template.

Let us create the Sliding Panel component in the bean. To refresh our memory, the bean is the Java class associated with the template.

Update your Layout.java class to include the SlidingPanel component, like this:

 package org.confucius.components;  
   
 import org.apache.tapestry5.annotations.Component;  
 import org.chenillekit.tapestry.core.components.SlidingPanel;  
   
 public class Layout {  
   
      @Component(parameters = {"subject=Chenillekit Greeter"})  
      private SlidingPanel panel1;  
        
 }  
   

Here, we have used annotations to tell Tapestry that 'panel1' is a SlidingPanel component, and its 'subject' property is 'Chenillekit Greeter'.

Now, update Layout.tml to use this Sliding Panel, like this:
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
   <head>  
     <title>Tapestry</title>  
   </head>  
   <body>  
     <h3>Basic Tutorial Series</h3>  
     
           <div t:id="panel1">  
                  <t:body/>  
           </div>  
   
           <p>Visit Us @ www.projectconfucius.org</p>  
             
   </body>  
 </html>  


If you rebuild and redeploy HelloWorldTapestry, you will see the Sliding Panel just like before. Thus, there is no difference in presentation whether you specify a component in the template or in code.

Typically, you will specify the component in code when you want to configure it dynamically, or if you want to keep your template clean of configurations.

Tapestry - Chenillekit

There are several open source component libraries for Tapestry, like Tynamo, TapX, Chenillekit, etc.

We will use Chenillekit as a demo, and put the greeting inside a Sliding Panel.

Update your pom.xml to contain the Chenillekit dependency, like this:

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
       <modelVersion>4.0.0</modelVersion>  
       <groupId>org.confucius</groupId>  
       <artifactId>HelloWorldTapestry</artifactId>  
       <packaging>war</packaging>  
       <version>0.0.1-SNAPSHOT</version>  
         
       <dependencies>  
           <dependency>  
                <groupId>org.apache.tapestry</groupId>  
                <artifactId>tapestry-core</artifactId>  
                <version>5.3.3</version>  
           </dependency>              
   
           <dependency>  
                <groupId>org.apache.tapestry</groupId>  
                <artifactId>tapestry5-annotations</artifactId>  
                <version>5.3.3</version>  
           </dependency>  
               
           <dependency>  
                <groupId>org.chenillekit</groupId>  
                <artifactId>chenillekit-tapestry</artifactId>  
                <version>1.3.3</version>  
           </dependency>  
         
   </dependencies>  
         
    <build>  
        <finalName>HelloWorldTapestry</finalName>  
    </build>  
         
 </project>  
   

Update Layout.tml to use a Sliding Panel, like this:

 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
   <head>  
     <title>Tapestry</title>  
   </head>  
   <body>  
     <h3>Basic Tutorial Series</h3>  
     
           <t:chenillekit.SlidingPanel t:subject="Chenillekit Greeter">  
             <t:body/>  
           </t:chenillekit.SlidingPanel>      
   
           <p>Visit Us @ www.projectconfucius.org</p>  
             
   </body>  
 </html>  

Now if you rebuild and redeploy HelloWorldTapestry, you will see the greeting inside a Chenillekit Sliding Panel.

Tapestry - Layouts

To create the same look-n-feel across multiple pages, Tapestry provides the concept of Layout. For example, all pages may have the same header and footer,

A Layout looks very similar to any other Page, except that it is placed in the /components folder instead of /pages.

For now, we will not worry about the difference between a component and a page - they are very similar. Both have a foo.tml file and a corresponding foo.java POJO.

Let us build a Layout for our pages.

In /src/main/resources/org/confucius, create a folder 'components'

In  /src/main/resources/org/confucius/components, create a file Layout.tml, like this:

 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
   <head>  
     <title>Tapestry</title>  
   </head>  
   <body>  
     <h3>Basic Tutorial Series</h3>  
     
     <t:body/>  
   
     <p>Visit Us @ www.projectconfucius.org</p>  
             
   </body>  
 </html>  


Note that we have included the Tapestry tag library. The magic here is the t:body tag - this will get automatically replaced by the content of the actual page.

Again, this looks just like any other Tapestry page, but because it is in the /components folder, Tapestry treats it like a 'component'.


In /src/main/java/org/confucius, create a folder 'components'

In  /src/main/java/org/confucius/components, create a class Layout.java, like this:

 package org.confucius.components;  
   
 public class Layout {  
   
 }  
   

This is just an empty POJO, but it is necessary to make it available to Tapestry.

Now, update your Index.tml page to use the Layout, like this:

 <html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">  
      <body>  
           <h2>${greeting}</h2>  
      </body>  
 </html>  
   


The t:type attribute tells Tapestry to use Layout.tml as the layout component.

If you now rebuild and redeploy HelloWorldTapestry, you will see the header and footer rendered in the page.

Note:
If some of your pages needed a different layout, say Layout2 - then you will need to create a Layout2.tml and Layout2.java, and in your pages, you will say t:type="layout2"

Friday, May 25, 2012

Tapestry - Using Beans

Due to Tapestry convention, Index.java is already a Bean, no configuration necessary.

Let us see how to use it in Index.tml.

Update your Index.java to have a property 'greeting', like this:
 package org.confucius.pages;  
 public class Index {  
      private String greeting;  
      public Index() {  
           this.greeting = "Hello World, Tapestry!";  
      }  
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
      public String getGreeting() {  
           return greeting;  
      }  
 }  

We can now use this field in Index.tml, like this:
 <html>  
 <body>  
 <h2>${greeting}</h2>  
 </body>  
 </html>  

Due to the one-to-one mapping between pages and Beans, Tapestry will look for the 'greeting' property in Index.java, since it is referred to in Index.tml

Tapestry will automatically call the getGreeting()) method to get the property 'greeting'.

If you rebuild and redeploy HelloWorldTapestry, and point your browser to:
http://localhost:8080/HelloWorldTapestry/

you will see "Hello World, Tapestry!" displayed.

Tapestry Hello World - Setup

Let us setup a Tapestry project using Maven.

In Eclipse, go to File --> New Project --> Maven Project

Use the maven-archetype-webapp

Specify:
Group Id: org.confucius
Artfact Id: HelloWorldTapestry
Package: org.confucius

Once the project is created, we will update the pom.xml to include the Tapestry libraries.

So your pom.xml will now look like this:
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
       <modelVersion>4.0.0</modelVersion>  
       <groupId>org.confucius</groupId>  
       <artifactId>HelloWorldTapestry</artifactId>  
       <packaging>war</packaging>  
       <version>0.0.1-SNAPSHOT</version>  
       <dependencies>  
           <dependency>  
                <groupId>org.apache.tapestry</groupId>  
                <artifactId>tapestry-core</artifactId>  
                <version>5.3.3</version>  
           </dependency>              
           <dependency>  
                <groupId>org.apache.tapestry</groupId>  
                <artifactId>tapestry5-annotations</artifactId>  
                <version>5.3.3</version>  
           </dependency>  
       </dependencies>  
       <build>  
        <finalName>HelloWorldTapestry</finalName>  
       </build>  
 </project>  

Next, we need to update our web.xml to use Tapestry.

Your web.xml will now look like this:
 <!DOCTYPE web-app PUBLIC  
  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"  
  "http://java.sun.com/dtd/web-app_2_3.dtd" >  
 <web-app>  
   <context-param>  
     <param-name>tapestry.app-package</param-name>  
     <param-value>org.confucius</param-value>  
   </context-param>  
   <filter>  
     <filter-name>Tapestry Filter</filter-name>  
     <filter-class>org.apache.tapestry5.TapestryFilter</filter-class>  
   </filter>  
   <filter-mapping>  
     <filter-name>Tapestry Filter</filter-name>  
     <url-pattern>/*</url-pattern>  
   </filter-mapping>  
 </web-app>  

We direct all URLs to be handled by the Tapestry filter.


The default Welcome page for Tapestry is Index.tml

Under /src/main/resources, create the folder structure:
/src/main/resources/org/confucius/pages

In /src/main/resources/org/confucius/pages, create a file Index.tml, like this:
 <html>  
 <body>  
 <h2>Hello World, Tapestry!</h2>  
 </body>  
 </html>  

Note that an TML file looks just like a HTML file.

Under /src/main, create a Source Folder called 'java'
(R-click on /src/main, select New-->Other, then select Java-->Source Folder)


Under /src/main/java, create the folder structure:
/src/main/java/org/confucius/pages


In /src/main/java/org/confucius/pages, create a class Index.java, like this:
 package org.confucius.pages;  
 public class Index {  
 }  

Note that this is an empty POJO, but it must be available to Tapestry - it is the matching POJO for Index.tml

Our Tapestry project is now setup.

If you build and deploy the project, and browse to
http://localhost:8080/HelloWorldTapestry/

You will see "Hello World, Tapestry!" displayed. 

Tapestry

Tapestry is very similar to JSF.

However, it uses Convention-Over-Configuration to greatly reduce configuration and simplify UI development.

Just like JSF Facelets, Tapestry has its own templating engine. Tapestry template files end in the .tml extension.

The most significant convention is that every page Foo.tml must have a corresponding Foo.java.

Foo.java is a POJO and is automatically a Tapestry Bean, no configuration is necessary.

Secondly, Foo.java can only be accessed from Foo.tml and not from any other template.

Thirdly, any method in Foo.java can return a class (say Bar.class), or a string (say "Bar"). This tells Tapestry that the next page to display is Bar.tml

These simple conventions go a long way in making a Tapestry project very organized.

For example, in JSF, since any page can refer to any Bean, very soon it leads to a criss-cross of references from pages to Beans. If a page is deprecated, it is not clear if any of the Beans need to be deprecated as well. In the absence of convention, pages and Beans become disconnected (eventually, a mess).

Tapestry, because if its one-to-one relation between pages and Beans, makes it very clear how pages can access Beans. If a page is deprecated, the corresponding Bean gets deprecated as well.

This is one example of how Tapestry conventions make things simpler. There are several others.

Another example, since a Bean is associated with exactly one page,  maintaining state is non-ambiguous. In JSF, if a Bean is called from several pages, some of its state may be set by one Page, other by another Page - leading to a chaotic situation.

Finally, building Tapestry components is way easier than building corresponding JSF components.

There are many other optimizations and scalability that Tapestry is able to deliver because of its well thought-out conventions.

Thursday, May 24, 2012

JSF - Dependency Injection

Just like Spring (see this post), JSF provides support for injecting Beans into other Beans.

Let us see how this is done.

We will move the method for creating a List of Greets to a new class, then inject this class into HelloBean.

In your /src/main/java/org/confucius, create a class called GreetDAO.java, like this:

 package org.confucius;  
   
 import java.util.ArrayList;  
 import java.util.List;  
   
 import javax.faces.bean.ManagedBean;  
 import javax.faces.bean.SessionScoped;  
   
 @ManagedBean(name="greetDAO_v1")  
 @SessionScoped  
 public class GreetDAO {  
      public List<Greet> getInternationalGreetsList(){  
           List<Greet> greetList = new ArrayList<Greet>();  
             
           Greet englishGreet = new Greet("English", "Hello World!");  
           greetList.add(englishGreet);  
             
           Greet frenchGreet = new Greet("French", "Bonjour tout le Monde!");  
           greetList.add(frenchGreet);  
             
           Greet italianGreet = new Greet("Italian", "Buongiorno a Tutti!");  
           greetList.add(italianGreet);  
             
           Greet spanishGreet = new Greet("Spanish", "Hola Mundo!");  
           greetList.add(spanishGreet);  
             
           Greet swahiliGreet = new Greet("Swahili", "Jambo!");  
           greetList.add(swahiliGreet);  
             
           return greetList;  
      }  
 }  
   

Note that we have declared this class to be a Bean named "greetDAO_v1"
(Typically, DAO classes connect to a database to get data objects, but here we are create the objects on the fly - for the purpose of demo)

Next, update your HelloBean to make use of this DAO class, like this:

 package org.confucius;  
   
 import java.util.List;  
   
 import javax.faces.bean.ManagedBean;  
 import javax.faces.bean.ManagedProperty;  
 import javax.faces.bean.SessionScoped;  
   
 @ManagedBean(name="helloBean")  
 @SessionScoped  
 public class HelloBean {  
      private String greeting;  
        
      @ManagedProperty(value = "#{greetDAO_v1}")  
      private GreetDAO greetDAO;  
   
      public HelloBean(){  
           this.greeting = "Hello World JSF!";  
      }  
        
      public String getGreeting() {  
           return this.greeting;  
      }  
   
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
   
      public GreetDAO getGreetDAO() {  
           return greetDAO;  
      }  
   
      public void setGreetDAO(GreetDAO greetDAO) {  
           this.greetDAO = greetDAO;  
      }  
   
      public String getInternationalGreets() {  
           return "international_greets";  
      }  
   
      public String getAmericanGreets() {  
           return "american_greets";  
      }  
        
      public List<Greet> getInternationalGreetsList(){  
           return greetDAO.getInternationalGreetsList();  
      }  
   
 }  
   

We have injected the GreetDAO bean into this class, and are using its method to return a List of Greet objects.

If you rebuild and redeploy HelloWorldJSF, you will be able to see the data table showing languages and greets.

JSF did what is necessary to provide a GreetDAO class instance to HelloBean.

JSF - Richfaces Datatable

A very common scenario in UI is to show a data table.

Usually a method gets a list of objects from a database, and we need to present them as a table.

Let us use Richfaces to display such a data table.

In your /src/main/java/org/confucius, create a class called Greet.java like this:

 package org.confucius;  
 public class Greet {  
      private String language;  
      private String greeting;  
      public Greet(String language, String greeting) {  
           this.language = language;  
           this.greeting = greeting;  
      }  
      public String getLanguage() {  
           return language;  
      }  
      public void setLanguage(String language) {  
           this.language = language;  
      }  
      public String getGreeting() {  
           return greeting;  
      }  
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
 }  

In your HelloBean.java, create a new method which returns a List of Greet objects, like this:

 package org.confucius;  
 import java.util.ArrayList;  
 import java.util.List;  
 import javax.faces.bean.ManagedBean;  
 import javax.faces.bean.SessionScoped;  
 @ManagedBean(name="helloBean")  
 @SessionScoped  
 public class HelloBean {  
      private String greeting;  
      public HelloBean(){  
           this.greeting = "Hello World JSF!";  
      }  
      public String getGreeting() {  
           return this.greeting;  
      }  
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
      public String getInternationalGreets() {  
           return "international_greets";  
      }  
      public String getAmericanGreets() {  
           return "american_greets";  
      }  
      public List<Greet> getInternationalGreetsList(){  
           List<Greet> greetList = new ArrayList<Greet>();  
           Greet englishGreet = new Greet("English", "Hello World!");  
           greetList.add(englishGreet);  
           Greet frenchGreet = new Greet("French", "Bonjour tout le Monde!");  
           greetList.add(frenchGreet);  
           Greet italianGreet = new Greet("Italian", "Buongiorno a Tutti!");  
           greetList.add(italianGreet);  
           Greet spanishGreet = new Greet("Spanish", "Hola Mundo!");  
           greetList.add(spanishGreet);  
           Greet swahiliGreet = new Greet("Swahili", "Jambo!");  
           greetList.add(swahiliGreet);  
           return greetList;  
      }  
 }  

Update your international_greets.xhtml to display the Greet objects in a data table, like this:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core"  
      xmlns:ui="http://java.sun.com/jsf/facelets"  
      xmlns:rich="http://richfaces.org/rich">  
       <ui:composition template="/templates/template.xhtml">  
           <ui:define name="title">International Greets</ui:define>  
           <ui:define name="content">  
                <h:form>  
               <rich:dataTable value="#{helloBean.internationalGreetsList}" var="greet" id="greettable">  
                      <rich:column>  
                        <f:facet name="header">  
                          <h:outputText value="Language"/>  
                                  </f:facet>  
                     <h:outputText value="#{greet.language}"/>                  
                      </rich:column>  
                      <rich:column>  
                        <f:facet name="header">  
                          <h:outputText value="Greeting"/>  
                                  </f:facet>  
                     <h:outputText value="#{greet.greeting}"/>                  
                      </rich:column>  
               </rich:dataTable>  
                </h:form>  
           </ui:define>  
   </ui:composition>  
 </html>  

The Richfaces DataTable will automatically iterate through the list, and populate the table.

If you rebuild and redeploy HelloWorldJSF, you will see a DataTable displaying all the greets in different languages.

JSF - Navigation

Let us see how to go from one page to another in JSF 2.0.

Note: JSF 1.2 had a different way of doing things

Let us create two new pages.

In your /src/main/webapp, create a file called international_greets.xhtml, like this:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core"  
      xmlns:ui="http://java.sun.com/jsf/facelets"  
      xmlns:rich="http://richfaces.org/rich">  
       <ui:composition template="/templates/template.xhtml">  
           <ui:define name="title">International Greets</ui:define>  
           <ui:define name="content">  
                <p>  
                English: Hello World!  
                <br/>  
                French: Bonjour tout le Monde!  
                <br/>  
                Italian: Buongiorno a Tutti!  
                <br/>  
                Spanish: Hola Mundo!  
                <br/>  
                Swahili: Jambo!  
                </p>  
           </ui:define>  
   </ui:composition>  
 </html>  


In your /src/main/webapp, create a file called american_greets.xhtml, like this:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core"  
      xmlns:ui="http://java.sun.com/jsf/facelets"  
      xmlns:rich="http://richfaces.org/rich">  
       <ui:composition template="/templates/template.xhtml">  
           <ui:define name="title">American Greets</ui:define>  
           <ui:define name="content">  
                <p>  
                Regular: Hi!  
                <br/>  
                Friendly: Whatsup!  
                <br/>  
                Extra Friendly: Hey, How are you doing!  
                </p>  
           </ui:define>  
   </ui:composition>  
 </html>  


Let us update our HelloBean.java to contain two new methods, which return the above pages by name.
Like this:
 package org.confucius;  
 import javax.faces.bean.ManagedBean;  
 import javax.faces.bean.SessionScoped;  
 @ManagedBean(name="helloBean")  
 @SessionScoped  
 public class HelloBean {  
      private String greeting;  
      public HelloBean(){  
           this.greeting = "Hello World JSF!";  
      }  
      public String getGreeting() {  
           return this.greeting;  
      }  
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
      public String getInternationalGreets() {  
           return "international_greets";  
      }  
      public String getAmericanGreets() {  
           return "american_greets";  
      }  
 }  

 Now, let us update out greet.xhtml to contain two buttons, which call the above methods.
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core"  
      xmlns:ui="http://java.sun.com/jsf/facelets"  
      xmlns:rich="http://richfaces.org/rich">  
       <ui:composition template="/templates/template.xhtml">  
           <ui:define name="title">JSF</ui:define>  
           <ui:define name="content">  
                <h1>#{helloBean.greeting}</h1>  
                <h:form>  
                     <h:commandButton value="Show International Greets" type="submit" action="#{helloBean.getInternationalGreets}" />  
                     <h:commandButton value="Show American Greets" type="submit" action="#{helloBean.getAmericanGreets}" />  
                </h:form>  
           </ui:define>  
   </ui:composition>  
 </html>  

Note that the Buttons have to be enclosed in a Form, otherwise they won't take effect.
This is a very common source of bugs!

If you rebuild and redeploy HelloWorldJSF, you will see the new buttons, which, when clicked direct you to the new pages.

JSF automatically assumes that the return values of the methods are names of pages, extension (.faces) is not required

Important Note:
Sometimes we use the full method name in the EL, like #{helloBean.getAmericanGreets}, while other times we leave out the 'get' part, like #{helloBean.greeting}. Why?

This is because when specified as an action, EL looks for a method with the exact name. When used outside an action specification, EL assumes it is a property field of the bean, and looks for the 'get' method.

Wednesday, May 23, 2012

JSF - Richfaces

Let us use a Richfaces component - Collapsible Panel to create some "special effects" in our UI.

Update your template.xhtml to envelope the content inside a Collapsible Panel, like this:
 <!DOCTYPE html>  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core"  
      xmlns:ui="http://java.sun.com/jsf/facelets"  
      xmlns:rich="http://richfaces.org/rich">  
   
      <h:head>  
           <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
           <title><ui:insert name="title">Default Title</ui:insert></title>  
      </h:head>  
   
      <h:body>  
           <h3>Basic Java Series</h3>  
           <rich:collapsiblePanel header="RichFaces Greeter" switchType="client">  
                <ui:insert name="content">Default content</ui:insert>  
           </rich:collapsiblePanel>       
           <p>Visit us at www.projectconfucius.org</p>  
      </h:body>  
 </html>  
   

Notice that we are including the richfaces tag library.

The "switchType" attribute specifies that the collapsibility should be managed in the browser, with no AJAX calls to the server. This makes the client respond faster, and the "View State" of the component is saved on the client side.

Let us also update our web.xml to choose a "skin" for richfaces - this customizes the look-and-feel of Richfaces components.

Your web.xml will not look like this:
 <?xml version="1.0" encoding="UTF-8"?>  
 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
   
   <context-param>  
     <param-name>org.richfaces.skin</param-name>  
     <param-value>blueSky</param-value>  
   </context-param>    
   
   <servlet>  
     <servlet-name>Faces Servlet</servlet-name>  
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>  
     <load-on-startup>1</load-on-startup>  
   </servlet>  
   
   <servlet-mapping>  
     <servlet-name>Faces Servlet</servlet-name>  
     <url-pattern>*.faces</url-pattern>  
   </servlet-mapping>  
   
   <welcome-file-list>  
     <welcome-file>greet.faces</welcome-file>  
   </welcome-file-list>  
   
 </web-app>  
   

If you rebuild and redeploy HelloWorldJSF, you will see  Richfaces Collapsible Panel, with a sky blue header, which collapses if you click on it.

JSF - Facelets

Facelets is a 'templating' technology for JSF - it allows us to:
  • Write XHTML files
  • Use JSF EL expressions to call methods on JSF Beans
  • Divide and Rule - compose pages from smaller XHTML pages
  • Use JSF Rich components 
We already saw the first two, now lets see how to compose pages from smaller XHTML pages.

This feature is very similar to Apache Tiles framework that we saw earlier in this post.

Let us suppose that we will need to create several pages with the same layout, but different content.

In your /src/main/webapp folder, create a folder 'template'

In /src/main/wepapp/template, create a file template.xhtml, like this:
 <!DOCTYPE html>  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core"  
      xmlns:ui="http://java.sun.com/jsf/facelets">  
   
      <h:head>  
           <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
           <title><ui:insert name="title">Default Title</ui:insert></title>  
      </h:head>  
   
      <h:body>  
           <h3>Basic Java Series</h3>  
           <ui:insert name="content">Default content</ui:insert>  
           <p>Visit us at www.projectconfucius.org</p>  
      </h:body>  
 </html>  
   

Notice that we have included the facelets tag library, under the name space 'ui'.

All our pages can use this template which automatically generates a header ("Basic Java Series") and footer ("Visit us..").

Each page can define its own title and content. We use the ui:insert tag to specify points at which a child XHTML page can insert its own content.

Let us update greet.xhtml to use this template, like this:
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core"  
      xmlns:ui="http://java.sun.com/jsf/facelets">  
   
       <ui:composition template="/templates/template.xhtml">  
   
           <ui:define name="title">JSF</ui:define>  
   
           <ui:define name="content">  
                <h1>#{helloBean.greeting}</h1>  
           </ui:define>  
   
   </ui:composition>  
 </html>  

We are using the ui:define tag to insert a title and content into the template.

If you now build and deploy HelloWorldJSF, and point your browser to:
http://localhost:8080/HelloWorldJSF/

you will see "Hello World, JSF!"  printed between the header and footer.
Notice also that the page title is now "JSF".

JSF - Bean Declaration

JSF allows you to declare your class as a Bean, then call its methods from the XHTML.

Under /src/main, create a Source Folder called 'java'
(R-click on /src/main, select New-->Other, then select Java-->Source Folder)

In /src/main/java, create a class HelloBean.java in a package org.confucius
(R-click on /src/main/java, select New-->Other, then select Java-->Class)

The class looks like this:
 package org.confucius;  
   
 import javax.faces.bean.ManagedBean;  
 import javax.faces.bean.SessionScoped;  
   
 @ManagedBean(name="helloBean")  
 @SessionScoped  
 public class HelloBean {  
      private String greeting;  
   
      public HelloBean(){  
           this.greeting = "Hello World JSF!";  
      }  
        
      public String getGreeting() {  
           return this.greeting;  
      }  
   
      public void setGreeting(String greeting) {  
           this.greeting = greeting;  
      }  
 }  
   

Note the annotations that declare this class to be a JSF Bean named "helloBean".

We will describe annotations in detail in an upcoming post. For now, note that annotations are like inline-configurations. Any framework can define its unique set of annotations. For the Java compiler, the annotations are like comments - it more or less ignores them. But it lets the framework know of the annotations and the framework can take whatever action it needs to take. In this case, JSF framework will look at the annotations and create a bean with the name 'helloBean' and make it session-scoped.


This bean is Session-scoped which means that its state will be maintained for the duration of the HTTP session.

We can now access the methods of this Bean from our XHTMLs.

Update greet.xhtml like this:
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core">  
   
   <h:body>  
           <h1>#{helloBean.greeting}</h1>  
      </h:body>  
 </html>  

This style of using #{expression} for calling a Bean's method is called JSF EL (Expression Language).
Facelets view handler will do the needful to convert the EL expression to appropriate content. 

If you rebuild and deploy HelloWorldJSF and point your browser to:
http://localhost:8080/HelloWorldJSF/

You will see "Hello World, JSF!" displayed.

JSF Hello World - Setup

Let us setup a JSF project using Maven.

Note that there are several open source projects which make JSF component libraries - like Trinidad, Tobago, Tomahawk, Openfaces, IceFaces.

In this project we will use JBoss Richfaces, which is one of the most popular ones and has a pretty big set of components.

In Eclipse, go to File --> New Project --> Maven Project

Use the maven-archetype-webapp

Specify:
Group Id: org.confucius
Artfact Id: HelloWorldJSF
Package: org.confucius

Once the project is created, we will update the pom.xml to include all the necessary JSF and Richfaces libraries.

So your pom.xml will now look like this:
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
       <modelVersion>4.0.0</modelVersion>  
       <groupId>org.confucius</groupId>  
       <artifactId>HelloWorldJSF</artifactId>  
       <packaging>war</packaging>  
       <version>0.0.1-SNAPSHOT</version>  
          
   <build>  
     <finalName>HelloWorldJSF</finalName>  
   </build>  
   
        <dependencyManagement>  
     <dependencies>  
       <dependency>  
         <groupId>org.richfaces</groupId>  
         <artifactId>richfaces-bom</artifactId>  
         <version>4.2.2.Final</version>  
         <scope>import</scope>  
         <type>pom</type>  
       </dependency>  
     </dependencies>  
   </dependencyManagement>  
   
   <dependencies>  
     <dependency>  
       <groupId>org.richfaces.ui</groupId>  
       <artifactId>richfaces-components-ui</artifactId>  
     </dependency>  
     <dependency>  
       <groupId>org.richfaces.core</groupId>  
       <artifactId>richfaces-core-impl</artifactId>  
     </dependency>  
     <dependency>  
       <groupId>javax.faces</groupId>  
       <artifactId>javax.faces-api</artifactId>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>org.glassfish</groupId>  
       <artifactId>javax.faces</artifactId>  
       <scope>compile</scope>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet</groupId>  
       <artifactId>javax.servlet-api</artifactId>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet.jsp</groupId>  
       <artifactId>jsp-api</artifactId>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>javax.el</groupId>  
       <artifactId>el-api</artifactId>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet.jsp.jstl</groupId>  
       <artifactId>jstl-api</artifactId>  
     </dependency>     
     <dependency>  
       <groupId>net.sf.ehcache</groupId>  
       <artifactId>ehcache</artifactId>  
     </dependency>  
   </dependencies>  
    
 </project> 

Notice that we are using a BOM (Bill of Materials), which is a Maven construct for projects that are version sensitive. BOMs specify the versions of its dependencies.

Explanation:
A specific version of RichFaces requires certain specific version of its dependencies. Mismatch in dependency versions can cause issues. For this reason, Richfaces defines a BOM which defines the versions of its dependencies. Therefore, in our POM, we do not need to specify the dependency versions.

Next, we need to update our web.xml to use JSF.

Your web.xml will now look like this:
 <?xml version="1.0" encoding="UTF-8"?>  
 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
   
   <servlet>  
     <servlet-name>Faces Servlet</servlet-name>  
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>  
     <load-on-startup>1</load-on-startup>  
   </servlet>  
   
   <servlet-mapping>  
     <servlet-name>Faces Servlet</servlet-name>  
     <url-pattern>*.faces</url-pattern>  
   </servlet-mapping>  
   
   <welcome-file-list>  
     <welcome-file>greet.faces</welcome-file>  
   </welcome-file-list>  
   
 </web-app>  
   

We direct all URLs which end in *.faces to be handled by JSF servlet.

Also, note that we have defined greet.faces as our welcome file.

However, we will never create a file called greet.faces - we will create one called greet.xhtml.

Whenever JSF sees a request for a file called foo.faces, it actually picks up foo.xhtml, sends it to the Facelets view handler which renders foo.html, which is sent back to the browser.

In your /src/main/webapp folder, create a file greet.xhtml, like this:
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
 <html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"  
      xmlns:f="http://java.sun.com/jsf/core">  
   
   <h:body>  
           <h1>Hello World!</h1>  
      </h:body>  
 </html>  

Note that an XHTML file looks just like a HTML file.

Note also that we have included two JSF tag libraries - HTML and Core. These tag libraries gives enhanced functionality over regular HTML tags.

Our JSF project is now setup.

If you build and deploy the project, and browse to
http://localhost:8080/HelloWorldJSF/

You will see "Hello World!" displayed.

JSF - Java Server Faces

JSF, a Sun standard View Technology., solves the Rich UI problem in the following ways:

1. JSF separates the "Rich component" developers from UI developers.

The Rich component developers deal with all the javascript and make the components cross-browser compatible. They provide a custom tag library which allows UI developers to use the components.

2. JSF provides a templating technology called Facelets.

Facelets files are .xhtml files which look just like regular HTML files. UI developers can use the custom tags provided by the Rich component developers to put components in their pages.

At run time, Facelets renders the final HTML page which is seen on the browser, replacing the custom tags with appropriate HTML tags and java scripts (as specified by the Rich component developers).

3. JSF provides a Bean container, so that any Java class on the server can be declared to be a JSF bean. And a rich component can call methods on the JSF Bean.

Internally, JSF generates all the necessary AJAX calls to make the communication between browser and server possible.

4. JSF allows page navigation to be declared in an XML file (faces-config.xml)

At run time, JSF will read the navigation file and display the appropriate pages to the user.

5. JSF maintains the state of the Beans and also of the View.

No effort is needed on part of the application developer.

View Technologies & Rich User Interfaces

The original Web UI was envisioned as hypertext, cross referencing, static web pages (sometimes called Web 1.0).

But things quickly progressed beyond that to give highly interactive, dynamically generated web pages with cool animation effects (sometimes called Web 2.0).

The technology which enabled this transition is javascript, which was a simple scripting language originally developed for small dynamic web page modifications, but people pushed it to its limits and beyond.

Such web interfaces are often called "Rich UI" and "Rich Application" because they display all kinds of special effects like collapsible Window Panels, Drag-n-Drop lists, interactive Charts, Dynamic Trees, Color themes, etc.

The components which display such complex dynamic effects are called "Rich components", and sometimes "Widgets".

Developing a Rich UI comes with a new set of challenges:

1. Developing complex javascript is not easy. It requires a lot of experience, it is not easy to debug and can result in unexpected behaviors in different browsers.

2. Rich components typically need to use AJAX to communicate with servers, which makes things more tricky.

3. In addition to application state, there is an additional need to maintain state of the Rich components (sometimes called View state)

4. There is always the browser "back" button which messes the view.

5. Finding an easy, seamless way for rich components to interact smoothly with Java objects on the application server is not trivial.

6. In the absence of ordinary hyperlinks, defining navigation between pages becomes a job in itself.

All these challenges led to the development of new frameworks for developing the front-end. These are often called "View Technologies".


In the coming posts, we will look at 3 of the most prominent view technologies:

 * JSF - Java Server Faces, is a Sun standard, and more widely used.

* Apache Tapestry, is the easiest to use and more elegant than JSF.

* GWT - Google Web Toolkit, is the most scalable.