In this chapter we'll build a Spring Surf application step-by-step together. In the first part of this tutorial, we will use Spring Roo shell to create, deploy and test a Surf application. We will then show you how to build the same Surf application using Spring Tool Suite. With Spring Tool Suite, we can either use Surf Spring Template Project approach to quickly setup our Spring Surf application or we can create it manually. Details of both approaches will be covered in this tutorial.
How to setup a Surf application from scratch.
Structure of a typical Surf application.
How to deploy, run and test a Surf application.
Surf Roo addon commands for creating new Surf pages, components, templates etc.
How to build a Surf application efficiently.
Joe Smith is a web developer working for a large manufacture company, Green Energy Corporation. Green Energy currently starts a new initiative to provide local communities with information about green energy related products and technologies. The key requirement of this new project is building an interactive and extensible web application that leverage latest web technologies and can be integrated with existing services that are available within Green Energy Corporation.
The project time line is very tight and Joe is the only resource assigned to this project. After doing some research, Joe decides to use Spring Surf for this project.
Once Joe has Spring Roo, Spring Developer Suite and Surf Developer Tools installed, he first tries to setup a basic Surf project for his future development. His goal is to get a basic Surf site running with a simple page. He also wants to find out how to deploy, run and test his application.
The first step for Joe is to create an empty directory and load Spring Roo:
$ mkdir community $ cd community $ roo
If Joe followed the installation instructions, he should be greeted with the Roo logo shown below:
____ ____ ____ / __ \/ __ \/ __ \ / /_/ / / / / / / / / _, _/ /_/ / /_/ / /_/ |_|\____/\____/ ABC [rev XYZ] Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER.
There are quite a few usability features within Roo. If Joe types "help" he will see all of the commands currently available (these change at different phases of your project lifecycle). Plus he can always press TAB in almost all cases for completion services. The first step for Joe is to type the "create project" command into the Roo shell:
roo> project --topLevelPackage com.greenenergy.community Created /Users/drq/dev/spring/roo/sandbox/community/pom.xml Created SRC_MAIN_JAVA Created SRC_MAIN_RESOURCES Created SRC_TEST_JAVA Created SRC_TEST_RESOURCES Created SRC_MAIN_WEBAPP Created SRC_MAIN_RESOURCES/META-INF/spring Created SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml Created SRC_MAIN_RESOURCES/META-INF/spring/log4j.properties
The required parameter for the "create project" command is the top level project name which, Joe will learn later, is also going to be used as the default Surf site name. For this case, the default Surf site name will be community. For this tutorial we will use SRC_MAIN_WEBAPP to reference the root directory of your web application.
As shown from the console output, Roo has created a Maven 2 project structure. Even if he quits Roo at this point and never reload it, at this moment Joe has a properly-configured Spring 3 web application, complete with URL rewriting, annotation-based classpath scanning and dependency injection of any class – even those created with the "new" keyword or via an ORM like Hibernate. He can even use "mvn jetty:run" to deploy and test the application on a local Jetty server. In general, Spring Roo helps developers to improve their productivities. However, they can always work on the project files directly using any editor or IDE.
With the basic Spring project ready, the "surf install" command will be available to Joe. This command will install all required Surf artifacts as well as a Quick Start Sample Site which Joe can use as a starting point.
Surf is a scriptable view composition framework for Spring MVC. It enables developers to quickly develop rich web application interfaces using templates and scripting. Surf provides view resolution for Spring MVC applications and integrates nicely with the Spring technology stack including Spring Web Flow, SpringSource Tool Suite and alternative view resolvers technologies.
Surf was initially developed by Alfresco Software Inc. in 2006 as its main web framework for products such as Alfresco Share, Alfresco Web Studio etc. Since November 2009, Surf has been available as a community extension of Spring.
Back to Joe's project, after running the "surf install" command, he gets following messages in Roo shell.
roo> surf install Created SRC_MAIN_WEBAPP/WEB-INF/config Created SRC_MAIN_WEBAPP/WEB-INF/config/surf-config.xml Created SRC_MAIN_WEBAPP/WEB-INF/config/surf-interop-config.xml Created SRC_MAIN_WEBAPP/WEB-INF/config/web-application-config.xml Created SRC_MAIN_WEBAPP/WEB-INF/web.xml Managed ROOT/pom.xml Created SRC_MAIN_WEBAPP/WEB-INF/urlrewrite.xml Created SRC_MAIN_WEBAPP/WEB-INF/surf.xml Managed ROOT/pom.xml Created SRC_MAIN_WEBAPP/surf-sample-site.zip Created SRC_MAIN_WEBAPP/css Created SRC_MAIN_WEBAPP/css/sample.css Created SRC_MAIN_WEBAPP/images Created SRC_MAIN_WEBAPP/images/alfresco3d.jpg Created SRC_MAIN_WEBAPP/images/AlfrescoLogo200.jpg Created SRC_MAIN_WEBAPP/images/AlfrescoLogo32.jpg Created SRC_MAIN_WEBAPP/images/background1.gif Created SRC_MAIN_WEBAPP/images/bg.gif Created SRC_MAIN_WEBAPP/images/chrome/box Created SRC_MAIN_WEBAPP/images/chrome/box/box_chrome_header_bg.gif Created SRC_MAIN_WEBAPP/images/cmis32.jpg Created SRC_MAIN_WEBAPP/images/cmis_logo_100.jpg Created SRC_MAIN_WEBAPP/images/gifts-and-gadgets.jpg Created SRC_MAIN_WEBAPP/images/our-services.jpg Created SRC_MAIN_WEBAPP/images/powered-by-spring.jpg Created SRC_MAIN_WEBAPP/images/PoweredBySurf.jpg Created SRC_MAIN_WEBAPP/images/products-overview.jpg Created SRC_MAIN_WEBAPP/images/surf32.jpg Created SRC_MAIN_WEBAPP/images/SurfLogo200.jpg Created SRC_MAIN_WEBAPP/WEB-INF/chrome/box Created SRC_MAIN_WEBAPP/WEB-INF/chrome/box/chrome.jsp Created SRC_MAIN_WEBAPP/WEB-INF/chrome/titled Created SRC_MAIN_WEBAPP/WEB-INF/chrome/titled/chrome.jsp Created SRC_MAIN_WEBAPP/WEB-INF/classes/surf/site/chrome Created SRC_MAIN_WEBAPP/WEB-INF/classes/surf/site/chrome/box.xml Created SRC_MAIN_WEBAPP/WEB-INF/classes/surf/site/chrome/titled.xml Managed SRC_MAIN_WEBAPP/WEB-INF/config/web-application-config.xml Created SRC_MAIN_WEBAPP/WEB-INF/pages/calendar Created SRC_MAIN_WEBAPP/WEB-INF/pages/calendar/calendar.xml Created SRC_MAIN_WEBAPP/WEB-INF/pages/home Created SRC_MAIN_WEBAPP/WEB-INF/pages/home/home.xml Created SRC_MAIN_WEBAPP/WEB-INF/pages/home/main.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/pages/home/main.get.head.ftl Created SRC_MAIN_WEBAPP/WEB-INF/pages/home/main.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/pages/home/side.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/pages/home/side.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/pages/products Created SRC_MAIN_WEBAPP/WEB-INF/pages/products/main.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/pages/products/main.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/pages/products/products.xml Managed SRC_MAIN_WEBAPP/WEB-INF/surf.xml Created SRC_MAIN_WEBAPP/WEB-INF/templates Created SRC_MAIN_WEBAPP/WEB-INF/templates/home.ftl Created SRC_MAIN_WEBAPP/WEB-INF/templates/home.xml Created SRC_MAIN_WEBAPP/WEB-INF/templates/landing.ftl Created SRC_MAIN_WEBAPP/WEB-INF/templates/landing.xml Created SRC_MAIN_WEBAPP/WEB-INF/templates/sample Created SRC_MAIN_WEBAPP/WEB-INF/templates/sample/login.ftl Created SRC_MAIN_WEBAPP/WEB-INF/templates/sample/logout.ftl Created SRC_MAIN_WEBAPP/WEB-INF/templates/sample/userinfo.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/calendar Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/calendar/calendar.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/calendar/calendar.get.head.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/calendar/calendar.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/footer Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/footer/footer.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/footer/footer.get.head.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/footer/footer.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/footer/footer.get.js Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/header Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/header/header.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/header/header.get.head.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/header/header.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/header/header.get.js Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/horizontal.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/horizontal.get.head.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/horizontal.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/horizontal.get.js Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/vertical.get.desc.xml Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/vertical.get.head.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/vertical.get.html.ftl Created SRC_MAIN_WEBAPP/WEB-INF/webscripts/navigation/vertical.get.js Created SRC_MAIN_WEBAPP/WEB-INF/classes/log4j.dtd Created SRC_MAIN_WEBAPP/WEB-INF/classes/log4j.xml Deleted SRC_MAIN_WEBAPP/surf-sample-site.zip Created SRC_MAIN_WEBAPP/WEB-INF/classes/surf/site/configurations Created SRC_MAIN_WEBAPP/WEB-INF/classes/surf/site/configurations/ default.site.configuration.xml
Joe didn't specify "siteName" parmeter for this "surf install" command so it will assume he wants to use community as the default value for both project name and Surf site name.
One thing that Joe has noticed is that all Surf configurations are placed into XML files. He can use any of his favorite XML editor or IDE to add, modify or delete them.
Before Joe moves on to next step, he spends some time going through all new files and tries to understand what they are for.
SRC_MAIN_WEBAPP\WEB-INF\web.xml
It defines a "UrlRewriteFilter" filter which Enables clean URLs with JSP views e.g. enabling url /welcome instead of /page/welcome.
Spring MVC Dispatcher Servlet is also defined and pointed to /WEB-INF/config/web-application-config.xml for its context configuration.
SRC_MAIN_WEBAPP\WEB-INF\urlrewrite.xml
This configuration file provides UrlRewriteFilter filter with a set of Surf related inbound and outbound url rewrite rules.
SRC_MAIN_WEBAPP\WEB-INF\surf.xml
It defines Surf specific configurations for runtime and mode. As default, it uses webapp runtime and development mode. (TODO: more details on the options)
SRC_MAIN_WEBAPP\WEB-INF\config\web-application-config.xml
As defined in web.xml, this file provides context configurations for the Spring MVC Dispatcher Servlet. It first imports required infrastructure imports from SRC_MAIN_WEBAPP\WEB-INF\config\surf-config.xml and then defines a list of required interceptors for the default Spring MVC annotation handler. Rest of the configurations are for interoperability with Spring annotated controllers and simple controllers. It also configues the default Spring multipart resolver for file uploading.
SRC_MAIN_WEBAPP\WEB-INF\config\surf-config.xml
This configuration file defines context locations for both Surf Web Scripts Framework and Surf Framework. It also sets up to be auto-resolved to url based views.
SRC_MAIN_WEBAPP\WEB-INF\config\surf-interop-config.xml
TODO: What is this file for?
SRC_MAIN_WEBAPP\css\sample.css
This is the style sheet of the Quick Start Sample Site.
SRC_MAIN_WEBAPP\images\*
Image files for the Quick Start Sample Site.
SRC_MAIN_WEBAPP\WEB-INF\chrome\*
Quick Start sample site provides two sample JSP chromes, box and titled. Chrome describes the border elements around a region or a component. These border elements may include styling elements like shadows, or they may introduce drag and drop capabilities into the page. They may also introduce user-functionality like buttons for popping up component settings (as you frequently see in portals).
SRC_MAIN_WEBAPP\WEB-INF\pages\*
To help users to get started with their project, Surf Quick Start Sample Site provides three sample pages, home, products and calendar. Surf only requires page configuration XMLs to be placed under SRC_MAIN_WEBAPP\WEB-INF. However for best practice it is highly recommended to place them under SRC_MAIN_WEBAPP\WEB-INF\pages and create a separated folder for each page using page ID as folder name, e.g. SRC_MAIN_WEBAPP\WEB-INF\pages\home.
A page is a navigable page in your web application. It may have associations to other pages and multiple formats keyed from it to templates. A page is a top-level object from which you can traverse either additional navigation or rendering paths.
SRC_MAIN_WEBAPP\WEB-INF\templates\*
Quick Start Sample Site is shipped with two sample Freemarker templates, home.ftl and landing.ftl, and corresponding template instances. A Template Instance is an instance of a template type, for instance, a Freemarker template for the Freemaker Template Type. A Surf page object has a required field for template instance therefore the Surf dispatcher knows which template to use to render view for the page. Other types of templates that Surf supports are JSP and Webscript.
SRC_MAIN_WEBAPP\WEB-INF\webscripts\*
This is the place for storing Websripts that generating components such header, footer,navigation etc. Some of the webscripts for page scoped components can also be placed in page directory. Again it is just for better organizing files.
A component is an instance of a component type that has been "bounded" into a region or a slot. It represents a binding along with the instance-specific state for that component instance. The Surf framework supports three types of scopes for the region/component binding, global scope, template scope and page scope. The Global Scope is for the component that is same across the site such as header and footer. The Template Scope is for the component that is same across the template such as a Text Block component which shows the same text for any page using this template. The page scope is for any page specific component.
SRC_MAIN_WEBAPP\WEB-INF\classes\log4j.*
These are log4j related configuration files that can be used for controlling logging levels.
SRC_MAIN_WEBAPP\WEB-INF\classes\surf\site\configurations\default.site.configuration.xml
This file defines site level configuration such as root page name, site name etc.
ROOT\pom.xml
On top of the pom.xml generated by "create project" command, Surf Roo addon adds two new dependencies to the pom.xml for getting all Surf required library jars.
With everything in place, Joe can't wait for giving it a try. Since Spring Surf is maven based, Joe doesn't need to worry about where to get required libraries for his project. Maven also lets Joe use a local tomcat server for building, deploying and testing the site he has so far.
Joe opens another terminal, loads Jetty and deploys the application war
mvn clean package jetty:run
Once it is deployed, Joe is ready to open his browser and visit his new Surf application for the first time. The home page url is http://localhost:8180/.
![]() |
Joe can also access Surf console to get a report on Surf objects as well as tools for launching Javacript Debugger, refreshing webscripts, templates and object registry etc.
![]() |
At this point, Joe has a running site with a few pages, templates and components. Joe also gets familar with the process of building, deploying and testing. To start his journey with Surf development, he decides to take a baby step first.
The first thing Joe will try to do is to customize the Sample site header and footer. He will use the existing templates and component webscripts that render the header and footer.
Joe first takes a look at following two websripts.
Websript for Footer: SRC_MAIN_WEBAPP\WEB-INF\webscripts\footer\footer.get.desc.xml SRC_MAIN_WEBAPP\WEB-INF\webscripts\footer\footer.get.head.ftl SRC_MAIN_WEBAPP\WEB-INF\webscripts\footer\footer.get.html.ftl SRC_MAIN_WEBAPP\WEB-INF\webscripts\footer\footer.get.js Websript for Header: SRC_MAIN_WEBAPP\WEB-INF\webscripts\header\header.get.desc.xml SRC_MAIN_WEBAPP\WEB-INF\webscripts\header\header.get.head.ftl SRC_MAIN_WEBAPP\WEB-INF\webscripts\header\header.get.html.ftl SRC_MAIN_WEBAPP\WEB-INF\webscripts\header\header.get.js
Before making changes to those two webscripts, Joe spends some time on learning Web Script. Joe learns that Web Scripts allow him to build custom URI-identified and HTTP accessible Web Services. A Web Script implementation consists of the following components: (often summarized as MVC):
A description document, which describes the URI that will initiate the script. For example, the service is given a short name and description, along with authentication and transactional needs. URI bindings are described as URI templates.
An optional controller script, written in JavaScript, which will do the actual work. For example, the script may query the external data repository to build a set of data items, known as a model to render in the response, or for URIs that intend to modify the repository (hopefully, PUT, POST and DELETE method bindings), the script may update the data repository. The JavaScript has access to all URI arguments, external services and repository data entry points.
One or more FreeMarker response templates, known as views (or Representations in REST parlance) that will render output in the correct format for your specific needs. For example, HTML, ATOM, XML, RSS, JSON, CSV, or any combination of these. The URL response is rendered via one of the supplied templates where the chosen template is based on the required response content-type or status outcome. The template has access to all URI arguments, common repository data entry points and any data items built by the optional controller script.
For the footer, Joe only wants to change the copyright message. Since the Javascript template of the footer webscript defines the value of the message as part of the "model" object which is then be passed on to the FreeMarker template for rendering.
After the change, Joe's new footer.get.js looks like
SRC_MAIN_WEBAPP\WEB-INF\webscripts\footer\footer.get.js model.message = "@ 2009 Green Energy, Co. All Rights Reserved.";
For the header, Joe changes the header title and adds a new tag line.
SRC_MAIN_WEBAPP\WEB-INF\webscripts\header\header.get.js model.headerTitle = "Green Energy"; model.tagLine = "Community Information Hub";
header.get.html.ftl
<div class="left">
<table width="100%">
<tr>
<td style="padding-left: 5px;padding-top: 20px;" nowrap>
<img src="${url.context}/res/images/agelogo.png" alt="GreenEnergy" /></td>
<td nowrap>
<div style="padding-left: 5px;padding-top: 20px; font-weight:bold;
font-size: 32px; font-family: Helvetica; color:#669900;">${headerTitle}</div>
<div style="padding-left: 5px;padding-top: 2px; font-weight:bold;
font-size: 16px; font-family: Helvetica; color:#669900;">${tagLine}</div>
</td>
<td width="100%"></td>
</tr>
</table>
</div>Joe also wants to change the styles of the header. For that he needs to make changes to SRC_MAIN_WEBAPP\WEB-INF\webscripts\header\header.get.head.ftl which is responsible for modifying or injecting styles, javascript includes etc. into the rendered HTML page head.
SRC_MAIN_WEBAPP\WEB-INF\webscripts\header\header.get.head.ftl
<style>
#header
{
padding: 5px,5px,5px,0px;
background-color: white;
border-top: 1px #bbb solid;
border-left: 1px #bbb solid;
border-bottom: 1px #bbb solid;
border-right: 1px #bbb solid;
background: #47b1ff url(res/images/homepage01.jpg) left top;
height: 124px;
}
</style>For the additional images(agelogo.png,homepage01.jpg), Joe puts them under SRC_MAIN_WEBAPP\images folder and they can be referenced in the Freemarker template simply as ${url.context}/res/images/IMAGE_FILE_NAME. In the URL, url.context is a Surf system property which can be used to retrieve the Surf web-server context path while /res is the url prefix defined as part of the URL rewrite rules.
After Joe made the changes and added additonal image files to the SRC_MAIN_WEBAPP\images folder, Joe refreshes and checkes the site home page to verify the changes are there.
New Header:
![]() |
New Footer:
![]() |
Joe made his first milestone of getting header and footer customized. During the process, Joe learns some basics about web scripts and understands footer and header are two template scoped components which are bonded to the two regions defined in the home template, SRC_MAIN_WEBAPP\WEB-INF\templates\home.ftl.
SRC_MAIN_WEBAPP\WEB-INF\templates\home.ftl
...
<div id="header" class="clearfix">
<@region id="header" scope="template" />
</div>
...
<div id="footer" class="clearfix">
<@region id="footer" scope="template" />
</div>
...Each Surf template can have one or many template instances. Each Surf page needs to be associated with one and only one template instance while a Surf template instance can be associated with multiple pages. Template instance is always defined as an XML document. A typical template instance XML consists of ID field, name field, decription field and all template scoped component definitions. This gives us great flexibility of reusing template for various scenarios. For example, we can use same two-columun layout for both home page and products page with different component configured for the same regions.
To create a new template, Joe needs to create a new Freemarker template. Joe starts with an HTML UI template which he got from his UI designer and added the region definitions into the places he want to make them rendered by Surf components. Surf Roo Addon provides a command for generating new template.
roo>surf template create --path news
As default, all templates will be placed under SRC_MAIN_WEBAPP\templates.
SRC_MAIN_WEBAPP\templates\news.ftl
<!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">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>${page.title}</title>
<link type="text/css" rel="stylesheet" href="${url.context}/res/css/sample.css" />
${head}
</head>
<body>
<div id="page">
<div id="header" class="clearfix">
<@region id="header" scope="template" />
</div>
<div id="horznav" class="clearfix">
<@region id="horznav" scope="template" />
</div>
<div id="content" class="clearfix">
<table width="100%">
<tr>
<td valign="top" nowrap width="20%">
<div id="vertnav">
<@region id="vertnav" scope="template" />
</div>
</td>
<td valign="top" width="60%">
<div id="center">
<@region id="center" scope="page" />
</div>
</td>
<td valign="top" width="20%">
<div id="right">
<@region id="right" scope="global" />
</div>
</td>
</tr>
</table>
</div>
<div id="footer" class="clearfix">
<@region id="footer" scope="template" />
</div>
</div>
</body>
</html>Above is three-column layout template that Joe creates. In addition to the header, footer and horizonal navigation region, it has three middle regions which are defined as template, page and global scope respectively.
With a new template ready, Joe wants to give it a try before creating and adding components to the regions.
Surf Roo addon provides two ways for creating a new page. First approach is to create a new template instance and then uses it to create a new page. The other approach is to create a new page directly from the template. If the new template instance id is not provided, it will assume that the new page will use the template instance with the same id as the template. If the template instance with the given or assumed id doesn't exist, the addon will create a new template instance with that id.
Here are some examples:
// Create a new page by only providing template id. surf page create --id news --template news
// Create a new template instance and place it under templates\news2 directory. surf template instance create --id news2 --path templates\news2 --template news // Create a new page using the template instance we just created and place it // under pages\news2. surf page create --id news2 --path pages\news2 --templateInstance news2
Joe takes the first approach so he runs following command in Roo shell:
surf page create --id news --template news
If everything goes as expected, Joe will get following message in the Roo shell.
roo> surf page create --id news --template news Created SRC_MAIN_WEBAPP\WEB-INF\news Created SRC_MAIN_WEBAPP\WEB-INF\news\news.xml New template instance created. Created SRC_MAIN_WEBAPP\WEB-INF\pages\news Created SRC_MAIN_WEBAPP\WEB-INF\pages\news\news.xml
The new page is now ready for viewing with the url http://localhost:8180/news. Surf provides easy URLs for pages. As Joe notices, he can simple append the page name to the home page URL. Joe has not configured anything for the page yet so he will only get a blank page as this point. But if he checks the source of the page, he will noticed the layout has been renderd with messages about missing component bindings.
Before setting up components for the new page, Joe wants to learn how to create his own component. For his requirement, he needs to create a "Green Enery News Feed" component for community users. This component needs to be able to invoke an online news service, retrieve related news feed in XML or JSON format and display the information in a nice HTML format.
Within the Surf framework, Joe will need to create a Webscript and mapped it to a URL which later can be bounded to his page as a component.
Joe starts with creating a new folder, newsfeed, under SRC_MAIN_WEBAPP\WEB-INF\webscripts. Webscript can be placed anywhere under SRC_MAIN_WEBAPP\WEB-INF\. This is just for file orgnization purpose. Once the folder is created, Joe creates all necessary files for this webscript.
<webscript> <shortname>News Feed</shortname> <description>News Feed</description> <url>/news/feed</url> </webscript>
//Define the remove service url
var newsServiceUrl = "http://www.renewableenergyworld.com/rss/renews.rss";
//Create an instance of remote connector. We will use http type.
var connector = remote.connect("http");
var re = /^http:\/\//;
if (!re.test(newsServiceUrl))
{
newsServiceUrl = "http://" + newsServiceUrl;
}
//Make the HTTP connection.
var result = connector.call(newsServiceUrl);
if (result !== null)
{
//Conver the result into String.
var rssXml = new String(result);
var re = /<[r|R][s|S]{2}/; // Is this really an RSS document?
//Make sure it is a RSS XML document.
if (re.test(rssXml))
{
// Strip out any preceding xml processing instructions or E4X will choke
var idx = rssXml.search(re);
rssXml = rssXml.substring(idx);
// It looks we need to get rid of the trailing junk as well.
if ( rssXml.indexOf('</rss>') != -1 ) {
rssXml = rssXml.substring(0,rssXml.indexOf('</rss>')+6);
}
// Parse the xml document using E4X APIs.
var rss = new XML(rssXml);
model.title = rss.channel.title.toString();
model.items = [];
var item, obj;
//Loop over all feed items.
for each (item in rss.channel..item)
{
//Retrieve field valuse and populate the JSON object that later will be
//passed on the the view template.
obj = {
"title": item.title.toString(),
"description": item.description.toString(),
"link": item.link.toString()
};
model.items.push(obj);
}
}
}
<div class="dashlet">
<div class="title" > ${title}</div>
<div class="body scrollableList">
<#if items?exists && items?size > 0>
<#list items as item>
<div style="clear:both;"><a href="item.link">${item.title}</a></div>
<div style="clear:both;">${item.description}</div>
</#list>
</#if>
</div><#-- end of body -->
</div><#-- end of dashlet -->
<style>
.dashlet
{
padding: 0px;
margin: 0px;
background-color: white;
}
.dashlet .title
{
padding: 5px 9px 5px 9px;
font-weight:bold;
}
.dashlet .body
{
overflow-x: hidden;
}
.dashlet .body .item
{
padding:5px;
}
.dashlet .body a,
.dashlet .body a:visited,
.dashlet .body a:hover
{
outline: none;
text-decoration: none;
}
.dashlet .body a:hover
{
text-decoration: underline;
}
.dashlet .scrollableList
{
height: 400px;
padding: 8px 0px;
overflow: auto;
margin-right: 1px;
}
</style>
Once Joe finishes the coding for the webscript, he can deploy and test the webscript. Each webscript is mapped to a unique URL. For this News Feed webscript, it will be mapped to http://localhost:8180/news/feed.
![]() |
Now Joe has built a nice Green Enery News Feed component. It is time to add it to the page he just created. The component will be placed in the center of the page and will be associated with a page scoped region. This means Joe will need to modify the page XML to add the new component configuration for the Green Enery News Feed component. Since it is page scoped, this configured component will be available only for this particular page.
For template scoped region, the component configuration will be placed in template instance xml which means any page that are based on this template instance will have same component configured.
For global scoped region, the component configuration will be placed in its own XMLs which as default needs to be place under SRC_MAIN_WEBAPP\WEB-INF\classes\surf\site\components. The configuration will be available across the application which means it needs to be configured once and only once.
Surf Roo addon provides several component related commands. For Joe's case, he needs to run following command in Roo shell.
roo> surf component create --page news --region center --url /news/feed Managed SRC_MAIN_WEBAPP\WEB-INF\pages\news\news.xml
As the message suggests, the change is made to template instnace news.xml and now Joe has the center region configued successfully.
![]() |
As Joe keeps working on his Surf site, he realizes that a lot of work is about configuring the pages. It would be very helpful to have commands that can show current page configuration as well as other related metadata.
Surf Roo addon provides a few report related commands. To generate and show report on his News page, Joe needs to run following command:
roo> surf report page --id news ----------------------------------------------------------- Report on Page news ----------------------------------------------------------- Basic Information Id: news Name: news Path: pages\news\news.xml Instance: news Template: news ----------------------------------------------------------- Page Scoped Components Region: center Url: /news/feed ----------------------------------------------------------- Template Scoped Components ----------------------------------------------------------- Other Components Region: right Scope: global Configued:false Region: horznav Scope: template Configued:false Region: vertnav Scope: template Configued:false Region: footer Scope: template Configued:false Region: header Scope: template Configued:false ----------------------------------------------------------- Page Associations -----------------------------------------------------------
The report shows Joe has configured one component and still needs to configure five other components for his News page. Before Joe configures header, foot and two navigation regions. Joe wants to explore a little more on global scoped region and component properties.
Joe first creates a new component which displays a HTML message which will be loaded from a component property. The new webscript will be mapped to /blocks/html and placed under SRC_MAIN_WEBAPP\WEB-INF\webscripts\html.
SRC_MAIN_WEBAPP\WEB-INF\webscripts\html\html.get.desc.xml (download) <webscript> <shortname>Html - Web Component</shortname> <description>Html - Web Component</description> <url>/blocks/html</url> </webscript>
SRC_MAIN_WEBAPP\WEB-INF\webscripts\html\html.get.js (download) var html = instance.properties["html"]; if(html != null) { html = html.replace('${url.context}', url.context); model.html = html; }
SRC_MAIN_WEBAPP\WEB-INF\webscripts\html\html.get.html.ftl (download) <div class="motd"> <div class="title">Message Of The Day</div> <div class="body"> <#if html?exists> ${html} <#else> Unable to find contents </#if> </div> </div>
SRC_MAIN_WEBAPP\WEB-INF\webscripts\html\html.get.head.ftl (download) <style> .motd { padding: 0px; margin: 0px; background-color: white; } .motd .title { padding: 5px 9px 5px 9px; font-weight:bold; } .motd .body { overflow-x: hidden; } </style>
Now Joe can associate it with the "right" region. Since it is global scoped, Surf will create a seperated XML file and as default the file will be placed under SRC_MAIN_WEBAPP\WEB-INF\classes\surf\site\components
roo> surf component create --page news --region right --url /blocks/html Created SRC_MAIN_WEBAPP\WEB-INF\classes\surf\site\components Created SRC_MAIN_WEBAPP\WEB-INF\classes\surf\site\components\global.right.xml
The component will load the display message from its "html" property. So Joe will have to edit the global.right.xml to add the property.
SRC_MAIN_WEBAPP\WEB-INF\classes\surf\site\components\global.right.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component>
<guid>global.right</guid>
<component-type-id>webscript</component-type-id>
<title>global.right</title>
<description>global.right</description>
<url>/blocks/html</url>
<resources/>
<scope>global</scope>
<source-id>global</source-id>
<region-id>right</region-id>
<properties>
<html>Welcome to Green Energy!</html>
</properties>
</component>
Joe also configures rest of regions for his News page.
roo> surf component create --page news --region header --url /company/header Managed SRC_MAIN_WEBAPP\WEB-INF\news\news.xml roo> surf component create --page news --region footer --url /company/footer Managed SRC_MAIN_WEBAPP\WEB-INF\news\news.xml roo> surf component create --page news --region horznav --url /navigation/horizontal Managed SRC_MAIN_WEBAPP\WEB-INF\news\news.xml roo> surf component create --page news --region vertnav --url /navigation/vertical Managed SRC_MAIN_WEBAPP\WEB-INF\news\news.xml
Now if Joe runs report on the News page again, it will show all regions have been configured.
----------------------------------------------------------- Report on Page news ----------------------------------------------------------- Basic Information Id: news Name: news Path: pages\news\news.xml Instance: news Template: news ----------------------------------------------------------- Page Scoped Components Region: center Url: /news/feed ----------------------------------------------------------- Template Scoped Components Region: header Url: /company/header Region: footer Url: /company/footer Region: horznav Url: /navigation/horizontal Region: vertnav Url: /navigation/vertical ----------------------------------------------------------- Global Scoped Components Region: right Scope: global Component:global.right Url:/blocks/html ----------------------------------------------------------- Page Associations -----------------------------------------------------------
After deploying and testing, Joe finally gets his page ready. Now he wants to explore Surf page association which is key for building site navigation. As we can see from the screen shot, Joe's news page is not on the horizonal navigation bar and left side vertical navigation menu is empty.
![]() |
Surf supports parent-child type of page associations. This allows Joe to designate a page as parent and have another page associated with it as child. For Surf site, the association will be available through Java and JavaScript APIS and thus can be used to build site page hierachy and site navigation.
As shown in the following code snippet, Surf allows navigation component webscript to get a list of child pages for current page and it can then be used for rendering site navigation.
model.pages = sitedata.findChildPages(context.pageId);
if (model.pages.length == 0)
{
// find the parent page
var parentPages = sitedata.findParentPages(context.pageId);
if (parentPages.length > 0)
{
model.pages = sitedata.findChildPages(parentPages[0].id);
}
}
To get the news page on the horizontal navigation bar, Joe will need to associate the page as child of the site root page. For this site, it will be the home page.
roo> surf page association create --sourceId home --destId news Managed SRC_MAIN_WEBAPP\WEB-INF\pages\home\home.xml
Now if Joe redeploys the site, he should see the News page show up on the horizontal navigation bar. For the vertical navigation menu, it shows a list of child pages of parent page of the news page since news page doesn't have any child pages.
![]() |
Now if Joe creates another page and makes it as a child of the news page, he will see the list of child pages on the left region.
roo> surf template instance create --id news2 --path templates\news2 --template news Created SRC_MAIN_WEBAPP\WEB-INF\templates\news2 Created SRC_MAIN_WEBAPP\WEB-INF\templates\news2\news2.xml roo> surf page create --id news2 --path pages\news2 --templateInstance news2 Created SRC_MAIN_WEBAPP\WEB-INF\pages\news2 Created SRC_MAIN_WEBAPP\WEB-INF\pages\news2\news2.xml roo> surf page association create --sourceId news --destId news2 Managed SRC_MAIN_WEBAPP\WEB-INF\pages\news\news.xml
As a side note, Joe can also add chorom and title configuration to the vertical navigation component so that it can render nicer view.
SRC_MAIN_WEBAPP\WEB-INF\news\news.xml
...
<component>
<region-id>vertnav</region-id>
<url>/navigation/vertical</url>
<chrome>box</chrome>
<title>Child Pages</title>
</component>
...![]() |
With a new page and several components built, Joe wants to package them and deploy it to his QA server. For Surf, packaging site is relatively easy task. All Joe needs to run is following maven command.
mvn clean package
Once it finishes, it will generate a WAR file which includes all needed library jars for running the site in any application server.
Joe is very happy with his experience with Surf so far. Now he plans to check out the IDE part of Surf Developer tools.
Spring Surf roo addon also provides addtional addons that Joe can install. To get a list of available addons, Joe would need to run following roo command in roo shell.
surf addon list
This command will display a list of addon ids with descriptions.
roo> surf addon list ID: php DESC: Adds support for scripting in Php. ID: groovy DESC: Adds support for scripting in Groovy. ID: studio DESC: Adds Spring Surf Web Studio. ID: documentation-core DESC: Adds Spring Surf documentation plugin. ID: documentation DESC: Adds Spring Surf documentation plugin and basic site templates.
If Joe wants to install any of above addons, he would need to run "surf addon install" command with id parameter.
For example, Joe wants to install the latest Spring Surf documentation plugin to his Surf project.
roo> surf addon install --id documentation Managed ROOT/pom.xml Managed ROOT/pom.xml Created ROOT/document-addon.zip Managed ROOT/src/docbkx/resources/css/html.css Managed ROOT/src/docbkx/resources/images/banner.png Managed ROOT/src/docbkx/resources/images/important.png Managed ROOT/src/docbkx/resources/images/note.png Managed ROOT/src/docbkx/resources/images/Thumbs.db Managed ROOT/src/docbkx/resources/images/tip.png Managed ROOT/src/docbkx/resources/images/xdev-spring_logo.jpg Managed ROOT/src/docbkx/resources/xsl/fopdf.xsl Managed ROOT/src/docbkx/resources/xsl/html.xsl Managed ROOT/src/docbkx/resources/xsl/html_chunk.xsl Managed ROOT/src/site/apt/index.apt Managed ROOT/src/site/apt/reference/index.apt Managed ROOT/src/site/docbook/reference/index.xml Managed ROOT/src/site/site.xml Deleted ROOT/document-addon.zip
Please note that after this addon installation, roo will need to be restarted. Once roo is restarted, Joe is ready to build site documentation using following maven command
mvn site
Once it finishs, Joe will get nice site reference documentation in mulitple formats such as HTML, singl-page HTML and PDF. All documents will include auto-generated Webscripts API docs, JavaScript API docs, Freemarker API docs etc.
As Joe has learned from the first part of the tutorial, Surf provides an addon for Spring Roo which allows him to quickly scaffold Surf pages and views on top of Spring MVC application. Since Spring Roo is bundled with Spring Tool Suite, he can use the same Surf roo commands as long as he installs the Surf Roo addon jar in STS.
Spring Tool Suite, as its name indicates, is a developer toolset building upon Eclipse. It provides Spring developers with a set of wizards, views, embedded servers etc. for higher productivity.
As part of Spring Tool Suite, Roo shell can be accssed through an Eclipse view. From STS menu, Joe can select Window -> Show View -> Other... -> SpringSource Tool Suite -> Roo Shell. Once that view is opened, it will show a list of Roo projects that are under his current workspace. He can then select the project and the Roo shell interface will be available to him.
There are multiple ways of setting up a Surf project within STS. Joe first tries to set it up manually.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Right click on the project and select Spring Tools -> Add Roo Project Nature The Dynamic Web Project is now set up to use Spring Roo.
![]() |
![]() |
![]() |
![]() |
With that, a very simple Spring project has been generated for Joe with a few important files and structures. These include Spring MVC application context settings, Maven build file (pom.xml)and Maven project structure.
Joe can now add Surf into his Spring MVC projects by simply using the Surf addon for Spring Roo. Just like he did within Roo shell, Joe installs Surf artifacts by running "surf install" command. This command will then update Maven build (pom.xml) to include Surf as a dependency, provide you with a /WEB-INF/surf.xml configuration file with Surf automatically configured for development mode, update /WEB-INF/urlrewrite.xml file to include necessary redirects for Surf, unpack a sample site, pages, templates and a few components to help Joe get started.
![]() |
The final step for Joe to do is to enable Maven dependency management for this project. By doing this, he allows STS to automatically download all of the necessary dependencies from pre-defined Maven repositories for his project.
![]() |
Joe needs to wait till Maven gets all required dependencies. Once it finishes, Joe is ready to deploy and test his Surf site.
![]() |
To deploy and test Surf project within STS, Joe can keep using the pre-configued Jetty server by running jetty:run command. He will need to right click on the project and select Run As -> Run Configurations.
Joe will then need to setup a new "Maven Build" launch configuration as shown in the following screen shot.
![]() |
Joe can also use SpringSource tc Server which comes with STS for his testing. He can either run the deploy Roo command within Roo shell
or
![]() |
![]() |
![]() |
The project will be built and deployed to the embedded Tomcat instance. When it starts up, Joe can point a browser to the following path: http://localhost:8080/community He will then see the home page of sample site.
![]() |
Surf also provides an additonal template for SpringSource Tool Suite Template Project Wizard. The STS template wizard lets Joe quickly create new Spring Surf projects through unpacking a prepacked STS project. It will save Joe all the manual steps.
The additonal Surf template is packaged as a custom STS/Eclipse plugin.The custom plugin extends extension point com.springsource.sts.content.core.descriptors. This will make Spring to include additional templates that are provided by Surf.
Just like rest of Spring template projects, the description XML file and acutal project zip files are hosted remotely. When users tried to install the template project for the first time, it will download the remote copy and save a local copy under .metadata/.sts/content directory of the current workspace. For each template project, it will create a sub directory using the template id as the directory name.
After the first time downloading, STS will use the local copy unless a newer version is available on the remote server. In that case, the newer version will be downloaded and the local copy will be updated.
![]() |
To verify the Surf project template has been installed through the custom plugin, Joe can
![]() |
![]() |
Once Joe selects the Surf project template and click Next button. If he is using it for the first time, it will popup a message box to confirm the download. Otherwise, it will show a dialog asking for Eclipse project name, Spring Surf site name and top level package. Joe enters valid inputs for all required fields and click Finish.
![]() |
STS will then unpack the project zip file and create a new project for Joe. This project will have Spring Roo shell nature enabled and a Surf project created with all samples from the Quick Start sample Site.
Just as the manual approach, the final thing for Joe to do is to enable Maven dependency management for this project.
Joe needs to wait till Maven does its job getting all required dependencies. Once it finises, Joe is ready to deploy and test his Surf site.