IntroToRSF - the Sakai wiki
Download
Report
Transcript IntroToRSF - the Sakai wiki
Introduction to
Reasonable Server Faces
Aaron Zeckoski
[email protected]
Creative Commons AttributionNonCommercial-ShareAlike 2.5 License
RSF Wiki
Why use RSF?
• Core RSF Values:
– Completely pure HTML templating
• Roundtrip with designers without loss
• Preview behaviour as well as appearance from
the file system
• Templates can be reloaded on a live server just
by dropping in files
– IoC is built-in
• No need to "integrate with" Spring, RSF is built
from Spring
• IoC in the request scope "refreshes the parts
other IoC cannot reach" (universal portability
without code changes)
2
Why use RSF? (contd.)
• Core RSF Values:
– Stateless design
• Lightweight EL system allows talk of data "in the
future" as well as via web services/AJAX
• Request scope is completely flushed at the end of
the request
• Sakai sessions can be > 50Mb per user. RSF
encourages designs where session sizes are
minimized
• Uses a more PHP-like "running off the DB" model
3
A Sample RSF template
<?xml version="1.0" encoding="UTF-8" ?>
<html><head><title>RSF Items</title></head><body>
Hello, <span rsf:id="current-user-name" >Current User</span>
<form rsf:id="listItemsForm" method="post">
<table class="listHier">
<tr rsf:id="item-row:" >
<td class="firstColumn">
<span rsf:id="item-title" >Non updateable item</span>
<a rsf:id="item-update" href="AddItem.html?id=1">New item title</a>
</td>
<td rsf:id="item-dateCreated" class="secondColumn">
Oct 29, 2007 10:26 AM
</td>
</tr>
</table>
</form>
</body></html>
4
How to use this template?
• In RSF, there is a complete separation
between markup and render logic
– Markup: held in an XHTML template
decorated with rsf:id markers
– Logic: held in a Spring-managed (Java) bean
known as a Producer
• The job of a Producer is to produce a tree of
elementary ("primitive") UI components
• To see how this all fits together, let's look
at the overall RSF app structure
5
RSF structure and code
6
RSF structure
•
•
•
•
•
•
•
•
The template (always html)
defines the interface
The producer (java or xml)
defines the view, populates the
template, and handles navigation
ViewParams define the values
passed between views (get)
The requestContext defines
producer and backing beans
The applicationContext defines
app scope beans and handles rsf
app config (via spring)
Backing beans handle action
processing
Logic layer beans can be
injected as defined in the context
xml files
Model is basic data POJO
Template
(html)
ViewParams
(java)
Producer
(java)
requestContext
(xml)
Backing Bean
(java)
applicationContext
(xml)
Logic Layer
(rest of app)
model
(java)
7
RSF templates
Template
(html)
• XHTML files
– Must be valid XML or runtime error will
occur
• No custom tags used or needed
– Truly is a pure XHTML template
• Only uses one custom attribute
– rsf:id - identifies this component for the
producer
8
Sample template
<?xml version="1.0" encoding="UTF-8" ?>
<html><head><title>RSF Items</title></head><body>
Hello, <span rsf:id="current-user-name" >Current User</span>
<form rsf:id="listItemsForm" method="post">
<table class="listHier">
<tr rsf:id="item-row:" >
<td class="firstColumn">
<span rsf:id="item-title" >Non updateable item</span>
<a rsf:id="item-update" href="AddItem.html?id=1">New item title</a>
</td>
<td rsf:id="item-dateCreated" class="secondColumn">
Oct 29, 2007 10:26 AM
</td>
</tr>
</table>
</form>
</body></html>
9
Producer
(java)
RSF producer
• Controls displays logic and populates the
template with dynamic information
• Defines a view uniquely
– By setting a ViewID
• Recommend you create a public static VIEW_ID
• Implements ViewComponentProducer
– Define start page by implementing DefaultView
– Implement NavigationCaseReporter to control
“submit” navigation
– Implement ViewParamsReporter to receive query
parameters from http request
10
Sample producer
public class ItemsProducer implements ViewComponentProducer, DefaultView {
public static final String VIEW_ID = "Items";
public String getViewID() {
return VIEW_ID;
}
private CrudPlusLogic logic;
public void setLogic(CrudPlusLogic logic) {
this.logic = logic;
}
public void fillComponents(UIContainer tofill, ViewParameters viewparams, ComponentChecker checker) {
UIOutput.make(tofill, "current-user-name", logic.getCurrentUserDisplayName());
UIForm listform = UIForm.make(tofill, "listItemsForm");
List<CrudPlusItem> l = logic.getAllVisibleItems();
for (CrudPlusItem item: l) {
UIBranchContainer itemrow = UIBranchContainer.make(listform, "item-row:");
if (logic.canWriteItem(item)) {
UIInternalLink.make(itemrow, "item-update", item.getTitle(),
new AddItemViewParameters(AddItemProducer.VIEW_ID, item.getId()) );
} else {
UIOutput.make(itemrow, "item-title", item.getTitle() );
}
UIOutput.make(itemrow, "item-dateCreated", item.getDateCreated() );
}
}
}
11
ViewParams
(java)
RSF ViewParams
• Controls the passing of data between
page views
– Represents query parameters (GET) and
URL trunk in the abstract
– extends SimpleViewParameters
• Should be used when data needs to be
sent from one view to another
– Uses URLs in the RESTful way they were
designed
– However, is abstract and typesafe
– Can be reused on multiple pages
12
Sample ViewParams
public class AddItemViewParameters extends SimpleViewParameters {
public Long id; // an identifier for an item
public AddItemViewParameters() {
}
public AddItemViewParameters(String viewID, Long id) {
this.id = id;
this.viewID = viewID;
}
public String getParseSpec() {
// include a comma delimited list of the
// public properties in this class
return super.getParseSpec() + ",id";
}
}
• Is a "Pea" (uses fields rather than getters/setters)
• getParseSpec method is unnecessary unless you want
fine control over URL structure
13
requestContext
(xml)
RSF requestContext
• Request Scope Context
– Often called RSAC
• All RSAC beans are lazy by default
• Standard Spring bean definition file
– Uses the Spring file parser code
– Only includes a subset of the standard
functionality to increase speed
• Location of this file is set in the web.xml
14
Sample requestContext
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- list the backing beans here -->
<bean id="itemsBean"
class="org.sakaiproject.crudplus.tool.ItemsBean"
init-method="init">
<property name="logic"
ref="org.sakaiproject.crudplus.logic.CrudPlusLogic" />
</bean>
<!-- list the producer beans here -->
<bean class="org.sakaiproject.crudplus.tool.producers.ItemsProducer">
<property name="logic"
ref="org.sakaiproject.crudplus.logic.CrudPlusLogic" />
</bean>
<bean class="org.sakaiproject.crudplus.tool.producers.AddItemProducer">
<property name="logic"
ref="org.sakaiproject.crudplus.logic.CrudPlusLogic" />
</bean>
</beans>
15
applicationContext
(xml)
RSF applicationContext
• A standard Spring bean definition file
– Puts the beans in the application context
• Mostly used for configuring RSF
– Define child of requestAddressibleParent to
specify beans which can be the target of EL
– Define child of beanScopeParent to create a new
session scope for session beans
– Define a child of CRITemplateResolverStrategy to
control the location of templates
• Location of this file set in web.xml
16
Sample applicationContext
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- For security purposes, only beans listed in the comma separated value list
may be the target of EL operations coming in over the request -->
<bean parent="requestAddressibleParent">
<property name="value" value="itemsBean, infoMessages"/>
</bean>
<!-- Put this backing bean in session scope -->
<bean id="messageBeanScope" parent="beanScopeParent">
<property name="copyPreservingBeans" value="infoMessages" />
<property name="exclusive" value="true" />
</bean>
</beans>
17
Backing Bean
RSF Backing Bean
(java)
• Typical bean with methods to handle actions
and public properties
– No RSF dependencies
– Store data needed for processing the user actions
using public properties
– Control data model objects with public properties
(using EL from producer)
– Process actions using methods
• Can interact with logic layer
– But so can the producers
• Created and destroyed in the request cycle
by default (recommended practice)
– Can override this behavior
18
Sample backing bean
public class ItemsBean {
public CrudPlusItem newItem = new CrudPlusItem();
public Map selectedIds = new HashMap();
...
private CrudPlusLogic logic;
public void setLogic(CrudPlusLogic logic) {
this.logic = logic;
}
...
public String processActionAdd() {
if (newItem.getHidden() == null) {
// null here means that the box was not checked
newItem.setHidden( DEFAULT_HIDDEN );
}
logic.saveItem(newItem);
return "added";
}
}
19
Web app basics
• 4 key things you need to do in a webapp
1. Output dynamic text
•
Render data to the screen
2. Loop structures
•
Output collection or render tables
3. Optional rendering of components
•
Render some components based on state
4. Trigger Actions
• User actions or data transmission
In RSF, these all are done using one part in the template
and another in the producer (and maybe the backing
bean for an action method)
20
Output dynamic text
1
Hello, <span rsf:id="current-user-name">Current User</span>
UIOutput.make(tofill, "current-user-name",
logic.getCurrentUserDisplayName());
• Uses an rsf:id on an HTML entity to show
where to place the dynamic text
– Does not have to be a span or div only!
• UIOutput will send the escaped string to
the ID location in the component tree
– Non-escaped output using UIVerbatim
• Avoid this unless truly necessary
21
Loop structure
2
Use UIBranchContainer and colon tags for looping structures
List<Item> l = logic.getAllVisibleItems();
for (Item item: l) {
UIBranchContainer itemrow =
UIBranchContainer.make(listform, "item-row:");
if (logic.canWriteItem(item)) {
UIInternalLink.make(itemrow, "item-update", item.getTitle(),
new AddItemViewParameters(AddItemProducer.VIEW_ID, item.getId()) );
} else {
UIOutput.make(itemrow, "item-title", item.getTitle() );
}
UIOutput.make(itemrow, "item-dateCreated", item.getDateCreated() );
}
<table class="listHier">
<tr rsf:id="item-row:">
<td class="firstColumn">
<span rsf:id="item-title">Non updateable item</span>
<a rsf:id="item-update" href="AddItem.html?id=1">New item title</a>
</td>
<td rsf:id="item-dateCreated">Oct 29, 2007 10:26 AM</td>
</tr>
</table>
22
Optional rendering
3
if (item.getHidden().booleanValue()) {
UIOutput.make(itemrow, "item-title", item.getTitle() );
}
<span rsf:id="item-title">Hidden item title</span>
• HTML entities are rendered if the
component is tied via the rsf:id
– If there is no UI component matching the id
then the renderer skips over it
• A UIOutput can also peer with a tag in the
template with further component children,
to swap in or out a whole block of markup
23
Trigger actions
4
<input rsf:id="add-update-item" type="submit"
value="Add/Update Item" />
UICommand.make(addupdateitem, "add-update-item",
"#{itemsBean.processActionAdd}");
public String processActionAdd() {
logic.saveItem(updateItem);
return "added";
}
• Use a normal submit button with an id
– Use UICommand to tie to an action method in a backing bean
using EL
– NB – "action methods" are considered an old-fashioned form of
programming – try to use BeanGuards instead (more later)
• Return string ties to a navigation case in the producer
24
RSF in practice
25
RSF experience
• RSF has a moderate learning curve
– Mostly unlearning poor practices
• UI components are comprehensive
– Cover all HTML entities, fairly flexible
• AJAX integration easy
– Designed to work well with AJAX and JS
• Works like a webapp should
– Normal REST, back button works
• Easy for UI designers to work with
26
RSF EL
#{requestwriteablebean.property1.subproperty2}
• A tiny subset of the functionality of JSF
Expression Language
– No logic in the expression
– Sometimes called Value Language (VL)
• Works with any bean in the request or
application context
• More info on the RSF EL page
URL: http://www2.caret.cam.ac.uk/rsfwiki/Wiki.jsp?page=EL
27
OTP (One True Path)
<input rsf:id="title-input" size="60" name="title" type="text"/>
UIInput.make(form, "title-input", “EntryLocator.1.title");
public class EntryLocator implements BeanLocator {
…}
<bean id="EntryLocator" class=“uk.ac.cam.blogwow.tool.otp.EntryLocator“ />
• Defines a single path (EL) to your data
• Points to a BeanLocator which allows you to
tell RSF where to find your data
– Point it at your logic/dao layer
• RSF 0.7.2+ has an automated
"EntityBeanLocator" which will map to most
DAO APIs
28
Internationalization (I18n)
<b rsf:id=“remove-item">Are you sure you want to remove item (title)?</b>
UIMessage.make(tofill, “remove-item",
"remove.item.text", new Object[] { item.getTitle() } );
remove.item.text=Are you sure you want to remove item ({0})?
• Well supported with UIMessage concrete class
– Also MessageLocator
– Simple cases can be handled in the template with
rsf:id="msg=key"
• Takes advantage of the standard Java language
properties bundle handling
• Uses the Spring MessageSource for resource
(properties file) loading
– Configurable in applicationContext
URL: http://www2.caret.cam.ac.uk/rsfwiki/Wiki.jsp?page=I18N
29
User feedback
<div rsf:id="message-for:*" class="alertMessage">
<ul style="margin:0px;"><li>Message for user here</li></ul>
</div>
messages.addMessage( new TargettedMessage(“user.message",
new Object[] { item.getTitle() },
TargettedMessage.SEVERITY_INFO));
user.message=New item saved ({0})
• Allows for messages generated in a
previous request to appear in the template
– No code is required in the receiving template,
only the element with the rsf:id
– Format of the output messages is
configurable
30
Javascript and AJAX
• RSF does not use AJAX for its own
purposes (not embedded) so there are no
issues with collisions
– There are some widgets and helpers that are
included with RSF which use AJAX though
• RSF includes a Javascript library to make
it easy to work with the RSF generated
element ids
– Can be tricky to work with because of the way
the branch nesting works
31
RSF component trees
• Binds the markup to the model
– rsf:id in the template
– EL in the producer
• Tree and components created and
destroyed during the request cycle
– Short lived, frees up resources
– Rooted in a view and only kept around long
enough to render the view
URL: http://www2.caret.cam.ac.uk/rsfwiki/Wiki.jsp?page=Component
32
RSF structure revisit
• The template is pure
XHTML
– Easy for UI designers
• The producer is simple
and cleanly defines a
view
• ViewParams abstract
out the passing of values
• The requestContext
and applicationContext
are pure spring config
Template
(html)
ViewParams
(java)
Producer
(java)
requestContext
(xml)
Backing Bean
(java)
applicationContext
(xml)
Logic Layer
– Can be mixed up
sometimes
• Backing beans are
really just simple beans
(rest of app)
model
(java)
33
RSF resources
• RSF Wiki (online documentation)
– http://www2.caret.cam.ac.uk/rsfwiki/
• RSF forums
– http://ponder.org.uk/rsf/
• RSF APIs
– http://saffron.caret.cam.ac.uk/projects/RSFUtil/apidocs/
• RSF SVN
– https://saffron.caret.cam.ac.uk/svn/
34
Questions?
• RSF
– http://www2.caret.cam.ac.uk/rsfwiki/
35