Differences between revisions 38 and 39
Revision 38 as of 2007-03-29 10:31:02
Size: 24777
Comment:
Revision 39 as of 2009-09-20 21:52:49
Size: 24818
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 6: Line 6:
This tutorial is based on Maven and Eclipse. The guidelines also apply to any other scenario, but you must find the appropriate way of doing it within your IDE. The attachment:ShaleClay.zip (you will need to add the dependent jar files to the WEB-INF/llib folder manually or by running mvn war:inplace) file contains a complete Maven/Eclipse project which you may use as basis for this. This tutorial is based on Maven and Eclipse. The guidelines also apply to any other scenario, but you must find the appropriate way of doing it within your IDE. The [[attachment:ShaleClay.zip]] (you will need to add the dependent jar files to the WEB-INF/llib folder manually or by running mvn war:inplace) file contains a complete Maven/Eclipse project which you may use as basis for this.
Line 36: Line 36:
To make Eclipse understand that this is an Eclipse project you need to run the following Maven2 command:[[BR]] To make Eclipse understand that this is an Eclipse project you need to run the following Maven2 command:<<BR>>
Line 45: Line 45:
attachment:ShaleClaydir1.jpg {{attachment:ShaleClaydir1.jpg}}
Line 55: Line 55:
attachment:ShaleClaydir2.jpg {{attachment:ShaleClaydir2.jpg}}
Line 59: Line 59:
attachment:ShaleClaydir3.jpg {{attachment:ShaleClaydir3.jpg}}
Line 65: Line 65:
attachment:ShaleClay4.jpg {{attachment:ShaleClay4.jpg}}
Line 69: Line 69:
attachment:ShaleClay5.jpg {{attachment:ShaleClay5.jpg}}
Line 75: Line 75:
attachment:ShaleClay6.jpg {{attachment:ShaleClay6.jpg}}
Line 81: Line 81:
attachment:ShaleClay7.png {{attachment:ShaleClay7.png}}
Line 96: Line 96:
attachment:ShaleClay8.jpg {{attachment:ShaleClay8.jpg}}
Line 210: Line 210:
If we take a closer look at it we see that it is devided into sections. These sections are the parts of the page that naturally lend tem selves to a functionalitygroup and as such are candidates for reuse. This is known as templating. The page is formatted as a “liquid design” layout. If you want to read up on this, a good place to start is [http://www.mardiros.net/liquid-css-layouts.html/ Carmen Mardios] If we take a closer look at it we see that it is devided into sections. These sections are the parts of the page that naturally lend tem selves to a functionalitygroup and as such are candidates for reuse. This is known as templating. The page is formatted as a “liquid design” layout. If you want to read up on this, a good place to start is [[http://www.mardiros.net/liquid-css-layouts.html/|Carmen Mardios]]
Line 386: Line 386:
Lets take a closer look at the bean {{{com.acme.test.TestViewController}}} that we are using for our backing bean. The first thing we notice is that it inherits from [http://shale.apache.org/shale-view AbstractViewController]. This class gives us some hooks (callbacks) into some Shale added lifecycle methods so that we can perform certain tasks that are relevant to that particular lifecycle. The next thing to notice is that is refers to a class Person. If you look in the {{{faces-config.xml}}} file again you will find the following declaration: Lets take a closer look at the bean {{{com.acme.test.TestViewController}}} that we are using for our backing bean. The first thing we notice is that it inherits from [[http://shale.apache.org/shale-view|AbstractViewController]]. This class gives us some hooks (callbacks) into some Shale added lifecycle methods so that we can perform certain tasks that are relevant to that particular lifecycle. The next thing to notice is that is refers to a class Person. If you look in the {{{faces-config.xml}}} file again you will find the following declaration:
Line 443: Line 443:
We have now been through a simple Shale/Clay application. It is recommended that you play around with it to see the effects of your changes. When you are ready to move on a natural step is to follow the [:CreatingClayComponents:Create Clay components tutorial] to learn how to create reusable Clay components for your pages. We have now been through a simple Shale/Clay application. It is recommended that you play around with it to see the effects of your changes. When you are ready to move on a natural step is to follow the [[CreatingClayComponents|Create Clay components tutorial]] to learn how to create reusable Clay components for your pages.

Getting started with Shale and Clay

Preface

This tutorial is based on Maven and Eclipse. The guidelines also apply to any other scenario, but you must find the appropriate way of doing it within your IDE. The ShaleClay.zip (you will need to add the dependent jar files to the WEB-INF/llib folder manually or by running mvn war:inplace) file contains a complete Maven/Eclipse project which you may use as basis for this.

The easiest way to get started with Shale and Clay is by starting with the Maven2 clay-starter archetype.

Since the archetype currently has not made its way into the distribution, you will need to get the archetype from the Shale Subversion repository located at:

https://svn.apache.org/repos/asf/shale/maven/trunk/archetypes/shale-clay-starter-archetype

After you have downloaded the sources, you need to run the following Maven2 command in the shale-starter-archetype directory:

mvn clean install.

This will build the archetype and install into your local repository so that it is available to you.

Create a directory where you want the project, ex. C:\My projects\ShaleClay. Open a shell (CMD) and type in (on one line):

mvn archetype:create -DarchetypeGroupId=org.apache.shale.clay -DarchetypeArtifactId=clay-starter-archetype -DarchetypeVersion=1.0-SNAPSHOT -DgroupId=com.acme.test -DpackageName=com.acme.test -DartifactId=shaleclay

What happens here is that Maven will create a project based on Shale and Clay. The parameters are:

  • archetypeGroupId – The archetype groupId

  • archetypeArtifactId – The archetype artifactId

  • archetypeVersion – The archetype version number

  • groupId – The groupId of you project

  • packageName – The default package name of your project – included source is placed here

  • artifactId - The groupId of you project

After you have done his, a project will have been created for you in a directory with the same name as you gave for the artifactId. Move to this directory.

To make Eclipse understand that this is an Eclipse project you need to run the following Maven2 command:

mvn eclipse:eclipse –Dwtpversion=1.5 

After running this command a couple of files and a directory will be created that is needed for Eclipse. Your layout should look something like this:

ShaleClaydir1.jpg

Lets start

Start Eclipse and open a workspace that points to C:\My projects\ShaleClay (or wherever you chose as a workspace for this project)

For Eclipse you now need to import the newly created project into the workspace:

Choose “File->Import”. In the next dialog select "General-Exisiting projects into workspace"

ShaleClaydir2.jpg

You should the get the following:

ShaleClaydir3.jpg

In the field "Select root directory" enter the name of you workspace or use the “Browse..” button to navigate to it. Eclipse should now list all available projects. Select the ShaleClay project and hit the “Finish” button.

You may see a red cross on your projectname if you have not defined the maven classpath variable “M2_REPO”. You can add it under "Window-preferences" and then "Java-Build Path-Classpath Variables"

ShaleClay4.jpg

Press the "New..." button. In the next dialog type inn M2_REPO in the name field and in the ”Path:” field you enter the path to your local Maven2 repository. Press "Ok" and "Ok" again in the next dialog.

ShaleClay5.jpg

Eclipse will then inform you that it has to rebuild the workspace. Press the "Yes" button.

If you still have a red cross after it has recompiled, check the default Java version in Eclipse. If you have Java5 as standard the cause is the compiler-level is mismatched with what Maven set when it generated the project. Go to the problems view, right click on the message and choose “Quick Fix”

ShaleClay6.jpg

Select "Change Java compiler level to 1.4" and press “Ok”.

Your project should now compile ok. Now comes the time to look at the project it self and how it is organized. If you expand your project it should look something like this:

ShaleClay7.png

In the Java sourcefolder (src/main/java) under the packagename you provided you should find two classes: Person and TestViewController.

In the resources folder (src/main/resources) you will find three property files. This is actually one property file with two language provisions of it. These can be identified by their name extention.

In the web sources folder (src/main/webapp) you will find several folders:

  • The folder ”images” contains the images and backgrounds that are used for the site.
  • The folder ”pages” contains standard definitions (defaultxxx.html) of the various parts that make up the site, along with some specific definitions (pageXbody.html)
  • The folder “templates” contains the template that we are using to build our site – we will return to this shortly
  • The folder ”theme” contains the cascading stylesheet we use to format our site.
  • In the folder ”WEB-INF” we find all the important configuration files.

ShaleClay8.jpg

Lets take a closer look at these, starting with web.xml where we find these important declarations:

   <!-- Override the default suffix for extension-mapped -->
   <context-param>
      <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
      <param-value>.jsf</param-value>
   </context-param>

   <!-- Select JSF State Saving Mode -->
   <context-param>
      <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
      <param-value>client</param-value>
   </context-param>

   <!-- Clay Common Configuration Resources -->
   <context-param>
      <param-name>
         org.apache.shale.clay.COMMON_CONFIG_FILES
      </param-name>
      <param-value>/WEB-INF/clay-config.xml</param-value>
   </context-param>

   <!-- Clay Configuration Full XML view Resources -->
   <context-param>
      <param-name>
         org.apache.shale.clay.FULLXML_CONFIG_FILES
      </param-name>
      <param-value>/WEB-INF/clay-views-config.xml</param-value>
   </context-param>

   <!-- Clay XML View Suffix -->
   <context-param>
      <param-name>
         org.apache.shale.clay.XML_TEMPLATE_SUFFIX
      </param-name>
      <param-value>.jsf</param-value>
   </context-param>

These declarations (context-param) are used by Clay and the JavaServer Faces (JSF) implementation at startup.

  • javax.faces.DEFAULT_SUFFIX – Tells which page suffixes should be handled by JSF
  • javax.faces.STATE_SAVING_METHOD – How should session state be persisted (Client-side or Server-side)
  • org.apache.shale.clay.COMMON_CONFIG_FILES – What are the name(s )of the Clay configuration file(s), providing absolute path within the web-context
  • org.apache.shale.clay.FULLXML_CONFIG_FILES – What are the name(s) of the Clay configuration file(s) that tells Clay about our page definitions.
  • org.apache.shale.clay.XML_TEMPLATE_SUFFIX – Which page suffixes should be handled by Clay.

Then are the Filter definitions. A filter is a Javaclass that will be called by the webcontainer on every request that comes into it (Actually only requests that match a certain predefined pattern – See filter-mapping below)

   <filter>
      <filter-name>shale</filter-name>
      <filter-class>
         org.apache.shale.application.faces.ShaleApplicationFilter
      </filter-class>
   </filter>

   <!-- Shale Application Controller Filter Mapping -->
   <filter-mapping>
     <filter-name>shale</filter-name>
     <url-pattern>/*</url-pattern>
   </filter-mapping>

Here Shale is wired into the request processing. In this case the Shale filter will be invoked on every request because of the

Next comes the Servlet definitions. These servlets are instandiated by the webcontainer at startup because they have a load-on-startup set. These Servlets are responsible for setting up various session and application scoped parameters based on the configuration files, and also for receiving all requests (That match a pattern) that comes into the webapplication

   <!-- JavaServer Faces Servlet Configuration --> 
   <servlet> 
      <servlet-name>faces</servlet-name> 
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup> 
   </servlet> 

   <!-- JavaServer Faces Servlet Mapping --> 
   <servlet-mapping> 
      <servlet-name>faces</servlet-name> 
      <url-pattern>*.jsf</url-pattern> 
   </servlet-mapping> 

Here the JSF Servlet is defined. It will be the handler of all requests that end with .jsf

Finally we tell the webcontainer how to handle our pages going back to the client. Since .jsf is not a well known mime-type, we need to inform the client that it should handle it as text/html.

        <mime-mapping>
                <extension>jsf</extension>
                <mime-type>text/html</mime-type>
        </mime-mapping>

The next file we look at is the faces-config.xml file. This file governs the setup of JSF. It is normally divided into three sections

  • General information - application
  • Navigation rules - navigation-rule
  • Managed beans - managed-bean

First in out file we define which languages this application supports; Norwegian(no) and English(en), and that the default is English.

The next section tells what should happen for various events in the application. We will closer into this a bit further down.

The last Section tells which beans JSF should manage for us. We will get closer into this a bit further down.

The next file we look at is clay-config.xml. This file contains definitions of our Clay components, amongst other the template that we use to construct our pages with and at this moment should be the only one defined.

Then we look at clay-views-config.xml. Here we define how the various pages on our site are build (composed), based on the components that we have defined in the clay-config.xml file. We have defined 3 separate pages. As you can see from the declarations are these based (extends) on the component “baseLayout” and that we override (reassign) the definitions of “title” and “bodyContent” in each definition.

We have now been through the main parts that make up a Shale/Clay application. We will now take a closer look at the pages we are going to use in our application. The first one being the template which makes up the look&feel of our application (you can have more than one template in your application) . This template is located in the folder “templates” and is named standard.html.

If we take a closer look at it we see that it is devided into sections. These sections are the parts of the page that naturally lend tem selves to a functionalitygroup and as such are candidates for reuse. This is known as templating. The page is formatted as a “liquid design” layout. If you want to read up on this, a good place to start is Carmen Mardios

The section that the page is divided into are:

  • Pagetitle
  • Leftmenu
  • Content
  • Footer

<body>
        <div id="head"><span jsfid="clay" clayjsfid="@headerContent"
                allowbody="false">Header Content</span></div>
        <div id='logo'>&nbsp;</div>
        <div id="left">&nbsp;</div>
        <div id="menu"><span jsfid="clay" clayjsfid="@leftContent"
                allowbody="false">Left Content</span></div>
        <div id="pad1">&nbsp;</div>
        <div id='content'><span jsfid="clay" clayjsfid="@bodyContent"
                allowbody="false">Body Content</span></div>
        <div id="pad1">&nbsp;</div>
        <div id="footer"><span jsfid="clay" clayjsfid="@footerContent"
                allowbody="false">Footer Content</span></div>
</body>

These sections are identifiable with the help of the attribute ”clayjsfid” on the <SPAN> tags. Here we see that we have: @headerContent, @leftContent. @bodyContent and @footerContent. These are what we refer to as symbols in Clay. This means that Clay processes the page looking for symbols and replaces them with their counterpart efinition in the configuration file(s). Lets look at how this template is defined as a Clay component in the clay-config.xml file:

        <component jsfid="baseLayout" extends="clay" id="base">
                <attributes>
                        <set name="clayJsfid" value="/templates/standard.html" />
                </attributes>
                <symbols>
                        <set name="title" value="Hello World" />
                        <set name="leftContent" value="/pages/defaultLeftNav.html" />
                        <set name="headerContent" value="/pages/defaultHeader.html" />
                        <set name="bodyContent" value="/pages/defaultBody.html" />
                        <set name="footerContent" value="/pages/defaultFooter.html" />
                </symbols>
        </component>

As we can see we have a section named “symbols”, Here we instruct Clay what to substitute the symbols with. In this case we have made some defaults to substitute in and the will be used unless we override them when defining a specific page. Now that we have a template to work with the work of creating specific pages start. Since we for now only want to replace the content part (defined by the symbol bodyContent) and the page title, we only need to create those parts. We now need to let Clay know about these. To tell Clay how to build our specific pages we do that in the clay-views.xml file. If we open this file, we will find several page definitions:

        <component jsfid="/page1.jsf" extends="baseLayout">
                <symbols>
                        <set name="title" value="Page 1" />
                        <set name="bodyContent" value="/pages/page1Body.html" />
                </symbols>
        </component>
        <component jsfid="/page2.jsf" extends="baseLayout">
                <symbols>
                        <set name="title" value="Page 2" />
                        <set name="bodyContent" value="/pages/page2Body.html" />
                </symbols>
        </component>
        <component jsfid="/page3.jsf" extends="baseLayout">
                <symbols>[
                        <set name="title" value="Page 3" />
                        <set name="bodyContent" value="/pages/page3Body.html" />
                </symbols>
        </component>

Here we define 3 components that inherit from our basecomponent “baseLayout”. As we mentioned we override the definitions of the title and bodyContent. We have now created 3 components based on a common template, but where the title and content vary. If we now look at for instance page1Body.html:

<h3>
    <span jsfid="outputText" value="#{messages['content.title.page1']}" allowBody="false">Clay template application - Page 1</span>
</h3>
<p>
    <span jsfid="outputText" value="#{messages['content.message.page1']}" allowBody="false">This is Page 1 content</span>
</p>

then we see that we refer to the jsfid ”outputText”. This is one of the standard predefined Clay components that we can utilize. It’s purpose is to output text. Further we refer to “#{messages['content.message.page1']}. This is a reference to a ”managed-bean” named messages. This is a bean that Shale will instantiate for us, If you open the faces-config.xml file you will find this at the bottom:

        <!-- Make resources available to the pages by defining it here (in a page use messages['propertyname'] as value -->
        <managed-bean>
                <managed-bean-name>messages</managed-bean-name>
                <managed-bean-class>
                        org.apache.shale.util.LoadBundle
                </managed-bean-class>
                <managed-bean-scope>application</managed-bean-scope>
                <managed-property>
                        <property-name>basename</property-name>
                        <value>
                                ResourceBundle
                        </value>
                </managed-property>
        </managed-bean>

Here we define the messages bean. In this case it is a special purpose Shale bean. Its purpose is to make available a resourcebundle that we have created, named ResourceBundle.properties. You will find it in folder src/main/resources. This bean is available application wide (given by the scope section). It supports internationalization (I18N), so we can have several versions of the file whose names differ by their extensions (_en, _no etc.).

Another attribute to note is “allowBody”. This attribute tells Clay whether or not to include any content within this tags body or not. Another way to achieve the same is through the Clay meta information <!-- ### clay:remove ### --> <!-- ### /clay:remove ### -->. One of the nice things about this that we can have mock content on a page so that during design time we get the full picture, and then have it removed during runtime so that it does not interfere with the actual content (Open the page in a browser to see this effect).

If we more on and look at the second page (/page2.jsf) and its content (pageBody2.html) we see that we have used more of Clays functionalities.

<h3>
    <span jsfid="outputText" value="#{messages['content.title.page2']}" allowBody="false">Clay template application - Page 2</span>
</h3>
<p>
    <span jsfid="outputText" value="#{messages['content.message.page2']}" allowBody="false">This is Page 2 content</span>
</p>
<span jsfid="form">
    <table>
        <tbody>
            <tr>
                <th>
                    <span jsfid="outputText" value="#{messages['name.label']}" allowBody="false">Please enter your name</span>
                </th>
                <td>
                    <input id="name" type="text" size="40" maxlength="50" value="#{@managed-bean-name.person.name}"/>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" action="#{@managed-bean-name.sayHello}" value="#{messages['page2.button.label']}">
                </td>
            </tr>
        </tbody>
    </table>
</span>

Here we see that we no longer only use the SPAN convenience tag, but the INPUT tag instead. Clay has 11 implicitly mapped tags. Those are html tags that it will check to see if they have been annotated with Clay attributes:

  1. <a> </a>

  2. <form> </form>

  3. <input type=txt>

  4. <input type=checkbox>

  5. <input type=radio>

  6. <input type=submit>

  7. <label> </label>

  8. <select> </select>

  9. <select multiple> </select>

  10. <option>

  11. <textarea> </textarea>

This means that one can use these tags and add the Clay specific attributes. Another thing to notice is that we have added a new symbol: @managed-bean-name. This symbol has special meaning in Shale/Clay. If we go back and look at the faces-config.xml file again, we will find the following declaration:

        <!-- Backing bean for page1 -->
        <managed-bean id="page1">
                <managed-bean-name>page1</managed-bean-name>
                <managed-bean-class>
                        com com.acme.test.TestViewController
                </managed-bean-class>
                <managed-bean-scope>request</managed-bean-scope>
        </managed-bean>
        <managed-bean id="page2">
                <managed-bean-name>page2</managed-bean-name>
                <managed-bean-class>
                        com.acme.test.TestViewController
                </managed-bean-class>
                <managed-bean-scope>request</managed-bean-scope>
                <managed-property>
                        <property-name>person</property-name>
                        <property-class>
                                com.acme.test.Person
                        </property-class>
                        <value>#{person}</value>
                </managed-property>
        </managed-bean>

What we do here is to define what is known as a backing beans or managed beans. These are beans that Shale will make available to us. Shale uses an implicit mapping strategy to decide which beans it should instantiate for a given view. If our page is called page1, it will then look for a managed bean with the same name and instantiate it. If you have a path to your page that spans several folders as in /foo/bar/pages/page1 then you need to define it as foo$bar$pages$page1 for Shale to find it and associate it with the page.

When Clay sees the symbol @managed-bean-name, it replaces it with the implicitly mapped bean that Shale is providing. In our case page1.

Lets take a closer look at the bean com.acme.test.TestViewController that we are using for our backing bean. The first thing we notice is that it inherits from AbstractViewController. This class gives us some hooks (callbacks) into some Shale added lifecycle methods so that we can perform certain tasks that are relevant to that particular lifecycle. The next thing to notice is that is refers to a class Person. If you look in the faces-config.xml file again you will find the following declaration:

        <managed-bean id="person">
                <managed-bean-name>person</managed-bean-name>
                <managed-bean-class>
                        com.acme.test.Person
                </managed-bean-class>
                <managed-bean-scope>session</managed-bean-scope>
        </managed-bean>

If you look at the above definition you will see that Person has been declared as a managed bean. Because Person is defined as a managed bean, Shale will inject it into our page2 definition because we instructed it to through the use of the managed-property section. Since Person has a scope of session it will be persisted between request so that any value that we set on one request will be available on the next. Finally we see that we define a method sayHello on the TestViewController bean, and that we refer to that method in the page2Body.html in the “action“ attribute (: action=#{@managed-bean-name.sayHello}).

One of the thing that separate JavaServer Faces from an ordinary webapplication is that most interaction with the server is through http POST. This means that all fields and actions must be surrounded with a form tag.

Another important thing to notice is that if declare your backing beans to extend AbstractViewController you have to declare them with request scope for the lifecycle methods to be called. If you declare it with scope session they will not be called!

When we do a post against the server JSF/Shale will populate all fields in the managed bean that we have associated with fields on the page (There is a lot of other things going on too but that is beyond the scope of this tutorial). That means that when the method sayHello is invoked a bean Person will have been instantiated and filled with the values coming from our page. In the method sayHello we do not actually do anything other that return a string. This string is used by JSF to figure out which view it should render. This is also defined in the faces-config.xml file under the navigation rules section.

        <navigation-rule>
                <!-- These pages should be accessable form everywhere -->
                <from-view-id>*</from-view-id>
                <navigation-case>
                        <from-outcome>home</from-outcome>
                        <to-view-id>/page1.jsf</to-view-id>
                </navigation-case>
                <navigation-case>
                        <from-outcome>page2</from-outcome>
                        <to-view-id>/page2.jsf</to-view-id>
                </navigation-case>
                <navigation-case>
                        <from-outcome>page3</from-outcome>
                        <to-view-id>/page3.jsf</to-view-id>
                </navigation-case>
        </navigation-rule>

In TestViewController.sayHello we returned the string page3, and in the navigation rules we see that this will send us to /page3.jsf. You can define many navigation rules, and also note that a navigation rule always must have a from view. If you use the asterix (*) then that will mean that unless otherwise specified the outcomes become global rules.

As a result of our action page3 will be rendered. If we look at the bodyCOntent definition for page3 in the clay-views-config.xml file we will se that it refers to page3Body.html.

<h3>
    <span jsfid="outputText" value="#{messages['content.title.page3']}" allowBody="false">Clay template application - Page 3</span>
</h3>
<p>
    <span jsfid="outputText" value="#{messages['content.message.page3']}" allowBody="false">This is Page 3 content</span>
</p>
<p>
    <span jsfid="outputText" value="#{messages['hello']}" allowBody="false">Hello</span> <span jsfid="outputText" value="#{person.name}" allowBody="false">World</span>
</p>

Here again we refer to the person bean, but this time we are displaying a value from it (#{person.name}. As we mentioned this bean has bean declared with session scope, so that the name we entered on page2 is now available on page3 for as long as our session exists. We can also refer to any other managed bean, and properties in these.

We have now been through a simple Shale/Clay application. It is recommended that you play around with it to see the effects of your changes. When you are ready to move on a natural step is to follow the Create Clay components tutorial to learn how to create reusable Clay components for your pages.

Hermod Opstvedt February 2007

ShaleAndClayTutorial (last edited 2009-09-20 21:52:49 by localhost)