Slide 1
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework
Colorado Software Summit: October 21 – 26, 2007
Extending JSF to Build a Product-
Specific UI Framework
Bryan Basham
StillSecure, Inc.
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 2
Colorado Software Summit: October 21 – 26, 2007
JavaServer Faces
by Gary Murphy
Choosing a JVM Web Framework
by Matt Raible
UI Design and Developement
by Bryan Basham
Introduction to Grails
by Scott Davis
Other Presentations
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 3
Colorado Software Summit: October 21 – 26, 2007
Topics Mind Map
Reviewof JSF
CustomComponents
DataConversion
DataValidation
ScreenManagement
ExtendingJSF
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 4
Colorado Software Summit: October 21 – 26, 2007
Raising the level of abstraction
Servlet / JavaServer Pages
JavaServer Faces
Cobia UI Framework
Cobia Module
request/response
tags
EL
components
conversion
validation
actions
domain-specific comps
screen management
browser control
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 5
Colorado Software Summit: October 21 – 26, 2007
JSF Fundamentals
Web container
JSF
.................................BackingBean
<jsp>.......</jsp>
ViewRoot
Form
InputText CmdLink...
request a view
creates
comp. tree
extractsdata
rendersview
POSTform data
bindsdata
performaction(s)
<jsp>.......</jsp>
select next view
createscomp. tree
rendersview
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 6
Colorado Software Summit: October 21 – 26, 2007
JSF Life Cycle
existing component tree
ViewRoot
Form
InputText CmdLink...
<jsp>.......</jsp>
new componenttree
FacesServlet
Lif
ecy
cle
RESTOREVIEW
APPLY REQVALUES
PROCESSVALIDATIONS
UPDATEMODEL
PROCESSAPPLICATION
RENDERRESPONSE
restoreState
processDecodes
processValid
ations
proces
sUpdat
es
proces
sAppli
cation
UICompTag
dispatchdoStart
doEnd
execute
render
saveState
encodeBegin
encodeEnd
encodeChildren
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 7
Colorado Software Summit: October 21 – 26, 2007
Topics Mind Map
Reviewof JSF
CustomComponents
DataConversion
DataValidation
ScreenManagement
ExtendingJSF
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 8
Colorado Software Summit: October 21 – 26, 2007
Example: IP address
Domain Model: EthernetInterface class
EthernetInterface
-String:roleName-String:ipAddress-String:netmask-String:macAddress
UI Screen: Edit Ethernet Interface
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 9
Colorado Software Summit: October 21 – 26, 2007
Example: IP address
Domain Model (better OO)
EthernetInterface
-String:roleName-Ipv4Address:ipAddress-NetMask:netmask-String:macAddress
Ipv4Address
-long:address
+mask(int bits):IP +inverseMask(bits):IP+compareTo(IP):int
Comparable<Ipv4Address>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 10
Colorado Software Summit: October 21 – 26, 2007
Converter in Use
<c:textField id="interfaceRole" value="#{editIntf.interface.role}" validator="#{editIntf.validateRole}" /><c:textField id="ipAddress" value="#{editIntf.interface.ipAddress}" required="true" converterId="converter.Ipv4Address" validator="#{editIntf.validateIpAddress}" /><c:textField id="netmask" value="#{editIntf.interface.netmask}" required="true" converterId="converter.NetMask" validator="#{editIntf.validateNetmask}" /><c:formOutput id="macAddress" value="#{editIntf.interface.macaddr}" />
JSF Container
Ipv4AddressConverter
10.20.30.42
10.20.30.47
ipAddress=“10.20.30.42”
ipAddress=“10.20.30.47”
getAsString()
getAsObject()
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 11
Colorado Software Summit: October 21 – 26, 2007
Converter Configuration
<?xml version="1.0"?><!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"><faces-config>
<converter> <description> Converts an Ipv4Address object to and from a String. </description> <converter-id>converter.Ipv4Address</converter-id> <converter-class> org.stillsecure.cobia.web.converters.Ipv4AddressConverter </converter-class> </converter>
<!-- more JSF configuration -->
</faces-config>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 12
Colorado Software Summit: October 21 – 26, 2007
Converter API
Ipv4AddressConverter
+getAsObject(...String):Object +getAsString(...Object):String
Converter
{from javax.faces.converter}
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 13
Colorado Software Summit: October 21 – 26, 2007
Converter.getAsObject
public Object getAsObject(FacesContext ctx, UIComponent comp, String displayString)throws ConverterException
{ Ipv4Address result = null; try { if ( displayString != null && displayString.trim().length() > 0 ) { result = new Ipv4Address(displayString); } } catch (IllegalArgumentException e) { FacesMessage message = MessageUtils.createFacesMessage(STRING_TO_OBJECT_FAILED, displayString); throw new ConverterException(message); } return result; }
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 14
Colorado Software Summit: October 21 – 26, 2007
Converter.getAsString
public String getAsString(FacesContext ctx, UIComponent comp, Object object)throws ConverterException
{ String result = null; if ( object == null ) return ""; try { Ipv4Address i = (Ipv4Address) object; result = i.toString(); } catch (ClassCastException e) { assert false : e; } return result; }
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 15
Colorado Software Summit: October 21 – 26, 2007
Topics Mind Map
Reviewof JSF
CustomComponents
DataConversion
DataValidation
ScreenManagement
ExtendingJSF
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 16
Colorado Software Summit: October 21 – 26, 2007
JSF provides field-level validation
Validation occurs after conversion
Form-level or multiple field validation
Use a hidden field at end of form
The validator method must extract data
from either components or request params
JSF Validation
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 17
Colorado Software Summit: October 21 – 26, 2007
Validation failures return to the form
and generates a user message:
Example: Roles are unique
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 18
Colorado Software Summit: October 21 – 26, 2007
Validation in Use
JSF Container
BackingBean
role=“Cobia [Internal]”
role=“”JSF
validateRole
ValidatorException
<c:textField id="interfaceRole" value="#{editIntf.interface.role}" validator="#{editIntf.validateRole}" /><c:textField id="ipAddress" value="#{editIntf.interface.ipAddress}" required="true" converterId="converter.Ipv4Address" validator="#{editIntf.validateIpAddress}" /><c:textField id="netmask" value="#{editIntf.interface.netmask}" required="true" converterId="converter.NetMask" validator="#{editIntf.validateNetmask}" /><c:formOutput id="macAddress" value="#{editIntf.interface.macaddr}" />
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 19
Colorado Software Summit: October 21 – 26, 2007
BackingBean.validateRole
public void validateRole(FacesContext ctx, UIComponent comp, Object value)throws ValidatorException
{ String input = value.toString(); String fieldId = component.getId();
// An empty role name is always legal if( input == null || input.length() == 0 ) return;
// The role name "disabled" is always legal if( input.compareToIgnoreCase( DISABLED_ETH_INTERFACE_ROLE ) == 0 ) return;
// more code on next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 20
Colorado Software Summit: October 21 – 26, 2007
BackingBean.validateRole// continued from last slide // Otherwise, verify that the role name is unique amongst this appliance's // other interface role names. boolean isUnique = true; for ( EthernetInterface interface : parentPage.getInterfaces() ) { if ( interface == currentInterface ) continue; if ( input.equals(interface.getRole()) ) { isUnique = false; break; } }
if( !isUnique ) { FacesMessage message = MessageUtils.createFacesMessage(ROLE_NOT_UNIQUE, input); throw new ValidatorException(fieldId, message, input ); } }
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 21
Colorado Software Summit: October 21 – 26, 2007
Topics Mind Map
Reviewof JSF
CustomComponents
DataConversion
DataValidation
ScreenManagement
ExtendingJSF
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 22
Colorado Software Summit: October 21 – 26, 2007
Custom look-and-feel
Support multiple clients (HTML, cell
phones, etc.)
Create complex components
Hide complex HTML
... raise the level of abstraction ...
Why build components?
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 23
Colorado Software Summit: October 21 – 26, 2007
Containers (screens, frames, divisions)
Output
Input
Complex (data tables)
Types of components
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 24
Colorado Software Summit: October 21 – 26, 2007
Tag class (e.g. TextFieldTag)
Component class (e.g. UITextField)
Renderer class (e.g. TextFieldRenderer)
Styles sheet (e.g. TextField.css)
JavaScript code (e.g. TextField.js)
Internatialization (e.g. text.properties)
Elements of components
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 25
Colorado Software Summit: October 21 – 26, 2007
Frame: a container comp.
<c:frame> <!-- PUT YOUR PAGE CONTENT HERE --></c:frame>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 26
Colorado Software Summit: October 21 – 26, 2007
Frame: elements
Framecomponent
UIFrame.class
FrameTag.class
FrameRenderer.class
div# {.......}
Frame.css
func(){ ...}
Frame.js
<f-c> ...</f-c>
faces-config.xml
<tag> ...</tag>
jsf-comps.tldabc=..xyz=..
text.properties
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 27
Colorado Software Summit: October 21 – 26, 2007
Frame: Tag class
package org.stillsecure.cobia.web.comps.tags;
import org.stillsecure.cobia.web.comps.UIFrame;import javax.faces.webapp.UIComponentTag;
public class FrameTag extends UIComponentTag{ public String getComponentType() { // Associates tag with component in faces-config.xml return UIFrame.COMPONENT_TYPE; }
public String getRendererType() { return UIFrame.RENDERER_TYPE; }
}
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 28
Colorado Software Summit: October 21 – 26, 2007
Frame: Comp. class
package org.stillsecure.cobia.web.comps;import org.stillsecure.cobia.web.comps.util.StyledComponent;import java.util.*;import javax.faces.component.UIComponentBase;
public class UIFrame extends UIComponentBase implements StyledComponent{ public static final String COMPONENT_TYPE = "component.Frame"; public static final String RENDERER_TYPE = "html.Frame"; @Override public String getRendererType() { return RENDERER_TYPE; } @Override public String getFamily() { return COMPONENT_TYPE; }// more code on next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 29
Colorado Software Summit: October 21 – 26, 2007
Frame: Comp. class
package org.stillsecure.cobia.web.comps;
import org.stillsecure.cobia.web.comps.util.StyledComponent;
public class UIFrame extends UIComponentBase implements StyledComponent{// more code on next slide
public Set<StyleInfo> getStyleInfo() { return STYLES; } private static final Set<StyleInfo> STYLES = new HashSet<StyleInfo>(); static { STYLES.add(new StyleInfo(StyleType.CSS, UIFrame.class.getSimpleName())); }}
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 30
Colorado Software Summit: October 21 – 26, 2007
Frame: Renderer class
package org.stillsecure.cobia.web.comps.renderers;
import java.io.IOException;import javax.faces.component.UIComponent;import javax.faces.context.FacesContext;import javax.faces.context.ResponseWriter;import javax.faces.render.Renderer;
public class FrameRenderer extends Renderer{ @Override public void encodeBegin(FacesContext context, UIComponent component) throws IOException { ResponseWriter out = context.getResponseWriter(); out.write(START_TEXT); } private static final String START_TEXT = "<div id='frameTLC'> \n" + " <div id='frameTRC'> \n";// code continued on next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 31
Colorado Software Summit: October 21 – 26, 2007
Frame: Renderer class
public class FrameRenderer extends Renderer{
// code continued from previous slide @Override public void encodeEnd(FacesContext context, UIComponent component) throws IOException { ResponseWriter out = context.getResponseWriter(); out.write(END_TEXT); } private static final String END_TEXT = " </div> \n" + " <div id='frameBLC'> \n" + " <div id='frameBRC'> </div> \n" + " </div> \n" + "</div> \n"; }
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 32
Colorado Software Summit: October 21 – 26, 2007
Frame: CSS file
/* ------ Frame Styles (Creates Gray Area of the Interface) ------------------ */div#frameTLC { /*Left top corner*/
background: url(../images/frame_TLC.gif) top left no-repeat;margin: 0 10px;width: 98%;min-width: 1024px;
}div#frameTRC { /*Right top corner*/
background: url(../images/frame_TRC.gif) top right no-repeat;padding: 10px 0 0 0;
}div#frameBLC { /*Left bottom corner*/
background: url(../images/frame_BLC.gif) bottom left no-repeat;}div#frameBRC { /*Bottom right corner*/
background: url(../images/frame_BRC.gif) bottom right no-repeat;margin: 0 0 0 10px;padding: 10px 0 0 0;
}
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 33
Colorado Software Summit: October 21 – 26, 2007
Frame: tag declaration
<?xml version="1.0" encoding="UTF-8" ?><taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"version="2.0">
<tlib-version>2.0</tlib-version> <short-name>cobia</short-name> <uri>http://cobia.stillsecure.org/jsf</uri> <tag> <description>
This tag represent the Frame component.This type of widget creates the gray, rounded-edgedstructure around the main body of the page.
</description> <name>frame</name> <tag-class>
org.stillsecure.cobia.web.comps.tags.FrameTag </tag-class> <body-content>scriptless</body-content> </tag></taglib>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 34
Colorado Software Summit: October 21 – 26, 2007
Frame: comp. declaration
<?xml version="1.0"?><!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"><faces-config>
<component> <component-type>component.Frame</component-type> <component-class>
org.stillsecure.cobia.web.comps.UIFrame </component-class> </component>
<renderer> <component-family>component.Frame</component-family> <renderer-type>html.Frame</renderer-type> <renderer-class>
org.stillsecure.cobia.web.comps.renderers.FrameRenderer </renderer-class> </renderer>
</faces-config>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 35
Colorado Software Summit: October 21 – 26, 2007
ImageList: an input comp.
Required flag
Rollover helpField message (not shown)
ImageList
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 36
Colorado Software Summit: October 21 – 26, 2007
ImageList: complex HTML
<div id="actionSection"> <label><span class="required">*</span> Action:</label> <span class="helpInfo"> <script language="javascript" type="text/javascript"> rolloverHelp('The <span class="reference">Action<\/span> is...'); </script> </span> <div id="actionContainer"> <script language="javascript" type="text/javascript">
var options = new Array();options[0] = new ImageListOption(/* image info, text, and value */);options[1] = new ImageListOption(/* next option data */);
// more optionsvar il = new ImageList("action", "ALLOW", options);il.writeHtml();
</script> </div></div>
<c:colorList id='ruleAction' required='true' type='org.stillsecure.cobia.module.firewall.domain.Action' value='#{editRule.rule.action}' />
UI Design HTML/JavaScript:
Encapsulated in a JSF component:
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 37
Colorado Software Summit: October 21 – 26, 2007
ImageList: elements
ImageListcomponent
UIImageList.class
ImageListTag.class
ImageListRenderer.class
div# {.......}
ImageList.css
func(){ ...}
ImageList.js
<f-c> ...</f-c>
faces-config.xml
<tag> ...</tag>
jsf-comps.tldabc=..xyz=..
text.properties
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 38
Colorado Software Summit: October 21 – 26, 2007
ImageList: model design
«enumeratedType»
Action{from Firewall module}
+ALLOW+DENY+REJECT
«interface»
UiImage
+getPrimaryImage():ImageData+getHighlightedImage():ImageData+getDisabledImage():ImageData
UIImageList
+setType(String):void+getType():Class<Enum>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 39
Colorado Software Summit: October 21 – 26, 2007
ImageList: component design
UIImageList
+setType(String):void+getType():Class<Enum>
ImageListTag
+setType(String)+setValue(String)+setRequired(boolean)#setProperties(UIComponent)
HtmlInputHidden{j.f.components.html}
+processDecodes+processValidators+processUpdates
UIFieldLabel
UIFieldMessage
UIRolloverHelp
EditableValueHolder{javax.faces.components}
children
UIComponentBase{javax.faces.components}
+getAttributes():List+saveState+restoreState
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 40
Colorado Software Summit: October 21 – 26, 2007
ImageList includes child components
UIComponentBase delegates lifecycle
behavior to children
UIComponentBase automatically stores
state of children
ImageList stores enumType in attributes
hash table
UIComponentBase automatically stores
state attributes hash table
ImageList: design evaluation
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 41
Colorado Software Summit: October 21 – 26, 2007
ImageList: Tag class
public class ImageListTag extends UIComponentTag{ public void setRequired(boolean required) { this.required = required; } private boolean required;
public void setValue(String value) throws JspException { if ( UIComponentTagUtils.isValueReference(value) ) { ValueBinding valueVB = ComponentUtils.createValueBinding(value); this.value = valueVB; } else { throw new JspException("The 'value' attribute must be a JSF value binding."); } } private ValueBinding value = null;// more code on next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 42
Colorado Software Summit: October 21 – 26, 2007
ImageList: Tag class
// code continued from previous slide public void setType(String enumType) { // Check the 'type' Class<Enum> enumClass = EnumUtils.checkType(enumType); if ( ! UiImage.class.isAssignableFrom(enumClass) ) throw new IllegalArgumentException("type must implement UiImage."); // Store it this.enumType = enumType; } private String enumType;
public void release() { super.release(); this.required = false; this.value = null; this.enumType = null; }// more code on next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 43
Colorado Software Summit: October 21 – 26, 2007
ImageList: Tag class
// code continued from previous slide protected void setProperties(UIComponent comp) { UIImageList component = (UIImageList) comp; // Handle basic attributes super.setProperties(component); component.setId(getId()); component.setRequired(this.required); // Process the 'value' value binding component.getInputField().setValueBinding(ComponentUtils.VALUE_ATTR, this.value); // Handle unique attributes component.setType(this.enumType); // Specify the converter EnumConverter converter = new EnumConverter(); converter.setType(this.enumType); component.setConverter(converter); }} // END of ImageListTag class
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 44
Colorado Software Summit: October 21 – 26, 2007
ImageList: Component class
public abstract class UIImageList extends UIComponentBase implements EditableValueHolder{ public void setType(String type) { // Check the 'type' Class<Enum> enumClass = EnumUtils.checkType(type); if ( ! UiImage.class.isAssignableFrom(enumClass) ) throw new IllegalArgumentException("type implement implements UiImage."); // Store the type name (and not the class object) to facilitate serialization this.getAttributes().put(TYPE_ATTR, type); } private static final String TYPE_ATTR = "typeAttr"; public Class<Enum> getType() { if ( this.enumClass == null ) { String type = (String) this.getAttributes().get(TYPE_ATTR); this.enumClass = EnumUtils.checkType(type); } return this.enumClass; } private transient Class<Enum> enumClass;// more code on the next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 45
Colorado Software Summit: October 21 – 26, 2007
ImageList: Component class
// code continued from previous slide
public HtmlInputHidden getInputField() { if ( this.childInput == null ) { String childID = String.format("%sInput", getId()); this.childInput = ComponentUtils.findComponentByClass(this, childID, HtmlInputHidden.class); if ( this.childInput == null ) { this.childInput = new HtmlInputHidden(); this.childInput.setId(childID); this.getChildren().add(this.childInput); } } return this.childInput; } private transient HtmlInputHidden childInput;
// more code on the next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 46
Colorado Software Summit: October 21 – 26, 2007
ImageList: Component class
// code continued from previous slide
// // EditableValueHolder methods // public void setConverter(Converter converter) { getInputField().setConverter(converter); } public void setValue(Object value) { getInputField().setValue(value); } public Object getValue() { return getInputField().getValue(); } // many other EVH methods; all delegate to the hidden input field
} // END of UIImageList component class
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 47
Colorado Software Summit: October 21 – 26, 2007
ImageList: Renderer class
public class ImageListRenderer extends Renderer{ public void encodeBegin(FacesContext context, UIComponent comp) throws IOException { UIImageList component = (UIImageList) comp; ResponseWriter out = context.getResponseWriter(); // Render the container <div> tag out.startElement(HTML.DIV_ELEM, component); out.writeAttribute(HTML.ID_ATTR, String.format("%sDIV", comp.getId()), null); // Encode field message encodeFieldMessage(context, component); // Encode the label encodeLabel(context, component); // Encode the help text encodeHelpText(context, component);
// Encode the hidden component tags <input type='hidden'... /> // This field holds the actual data for this component. encodeInputField(context, component);
// Encode the JavaScript for the list HTML component encodeList(context, component); // Encode JavaScript functions used by this component encodeSetFunction(context, component); }// more code on the next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 48
Colorado Software Summit: October 21 – 26, 2007
ImageList: Renderer class
// code continued from previous slide
public boolean getRendersChildren() { return true; } public void encodeChildren(FacesContext context, UIComponent component) throws IOException { // do nothing; all children are hidden } public void encodeEnd(FacesContext context, UIComponent comp) throws IOException { ResponseWriter out = context.getResponseWriter(); // Encode the end <div> tag out.endElement(HTML.DIV_ELEM); }
// more code on the next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 49
Colorado Software Summit: October 21 – 26, 2007
ImageList: Renderer class
// code continued from previous slide
private void encodeInputField(FacesContext context, UIImageList component) throws IOException { ResponseWriter out = context.getResponseWriter(); out.write(" "); component.getInputField().encodeBegin(context); component.getInputField().encodeEnd(context); out.write('\n'); }
private void encodeLabel(FacesContext context, AbstractSpecialtyList component) throws IOException { ResponseWriter out = context.getResponseWriter(); out.write(" "); component.getLabel().encodeBegin(context); component.getLabel().encodeEnd(context); out.write('\n'); }
// more code on the next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 50
Colorado Software Summit: October 21 – 26, 2007
ImageList: Renderer class
// code continued from previous slide private void encodeList(FacesContext context, UImageList component) throws IOException { String compID = component.getId(); ResponseWriter out = context.getResponseWriter();
out.write(String.format(COMP_START_TEMPLATE, compID)); // Create JS variables out.write(String.format(COMP_CREATE_VARS_TEMPLATE, compID, component.getValue())); // Create each image option int index = 0; for ( Enum optionVal : component.getType().getEnumConstants() ) { UiImage option = (UiImage) optionVal; ImageData primaryImage = option.getPrimaryImage(); ImageData highlightedImage = option.getHighlightedImage(); // Create the ImageListOption out.write(String.format(COMP_CREATE_AN_OPTION_TEMPLATE, compID, index, /* other data to template */)); // Increment the index index++; }// more code on the next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 51
Colorado Software Summit: October 21 – 26, 2007
ImageList: Renderer class
// code continued from previous slide // Create COMP variable out.write(String.format(COMP_CREATE_COMP_VAR_TEMPLATE, compID)); // Encode the end of the component's JavaScript out.write(String.format(COMP_END_TEMPLATE, compID));
} // END of encodeList method
private static final String COMP_START_TEMPLATE // 1$=compID = " <div id='%1$sContainer'> %n" +" <script language='javascript' type='text/javascript'> %n"; private static final String COMP_CREATE_VARS_TEMPLATE // 1$=compID 2$=current value = " var %1$sSelectedValue = '%2$s'; %n" +" var %1$sImageOptions = new Array(); %n";
private static final String COMP_CREATE_AN_OPTION_TEMPLATE = " %1$sImageOptions[%2$d] %n" +" = new ImageListOption( %n" +" new ImageListIcon('%3$s', %4$d, %5$d), %n" +" new ImageListIcon('%6$s', %7$d, %8$d), %n" +" '%9$s', '%10$s'); %n"; private static final String COMP_CREATE_COMP_VAR_TEMPLATE // 1$=compID = " var %1$sComponent = new ImageList('%1$s', %1$sSelectedValue, %1$sImageOptions); %n";
private static final String COMP_END_TEMPLATE // 1$=compID = " %1$sComponent.writeHtml(); %n" +" </script> %n" +" </div> %n";
// more code on the next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 52
Colorado Software Summit: October 21 – 26, 2007
ImageList: Renderer class
// code continued from previous slide
private void encodeSetFunction(FacesContext context, UIImageList component) throws IOException { ResponseWriter out = context.getResponseWriter(); String compID = component.getId(); String inputFieldID = component.getInputField().getClientId(WebContext.getFacesContext()); out.write(String.format(SET_FUNCTION_TEMPLATE, compID, inputFieldID)); } private static final String SET_FUNCTION_TEMPLATE // 1$=compID 2$=clientID = " <script language='javascript' type='text/javascript'> %n" +" function set_%1$s() { %n" +" var value = %1$sComponent.getSelectedValue(); %n" +" $('%2$s').value = value; %n" +" } %n" +" navigation.addSubmitAction(set_%1$s); %n" +" </script> %n";
} // END of ImageListRenderer class
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 53
Colorado Software Summit: October 21 – 26, 2007
ImageList: design recap
UIImageList
+setType(String):void+getType():Class<Enum>
ImageListTag
+setType(String)+setValue(String)+setRequired(boolean)#setProperties(UIComponent)
HtmlInputHidden{j.f.components.html}
+processDecodes+processValidators+processUpdates
UIFieldLabel
UIFieldMessage
UIRolloverHelp
EditableValueHolder{javax.faces.components}
children
UIComponentBase{javax.faces.components}
+getAttributes():List+saveState+restoreState
ImageListRenderer
+encodeBegin+getRendersChildren+encodeChildren+encodeInputField ...+encodeList+makeSetFunction
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 54
Colorado Software Summit: October 21 – 26, 2007
EntitySet: a complex comp.
<c:sectionHeading id='ripRoutes' /><c:entitySet id='ripInterfacesTable' value='#{ripMgr.ripInterfaces}' var='rip'
allowsAdd='true' allowsEdit='true' allowsDelete='true'> <c:column id='interface' value='#{rip.ethInterface.device}' sort='true' /> <c:column id='interfaceStatus' value='#{rip.ethInterface.status}' sort='true' /> <c:column id='interfaceRole' value='#{rip.ethInterface.role}' sort='true' /> <c:column id='ipAddress' value='#{rip.ethInterface.ipAddress}' sort='true' /> <c:column id='interfaceNetmask' value='#{rip.ethInterface.netmask}' sort='false' /> <c:column id='ripEnabled' value='#{rip.enabled}' sort='false' /> <c:column id='metric' value='#{rip.metric}' sort='true' type='Numeric' /> <c:column id='authentication' value='#{rip.authentication.type}' sort='false' /></c:entitySet>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 55
Colorado Software Summit: October 21 – 26, 2007
Represents a table of entity objects
Includes controls to add, edit, and/or
delete objects from the set
Includes subcomponents to control columns
Includes controls to sort on a specific column
EntitySet: a complex comp.
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 56
Colorado Software Summit: October 21 – 26, 2007
EntitySet: elements
EntitySetcomponent
UIEntitySet.class
EntitySetTag.class
EntitySetRenderer.class
div# {.......}
EntitySet.css
func(){ ...}
EntitySet.js
<f-c> ...</f-c>
faces-config.xml
<tag> ...</tag>
jsf-comps.tldabc=..xyz=..
text.properties
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 57
Colorado Software Summit: October 21 – 26, 2007
EntitySet: model design
UIEntitySet
-VB=#{ripMgr.ripInterfaces}
introspect to extract
column data
«backingBean»
RipInterfaceManager{from Router web package}
+getRipInterfaces():List+setRipInterfaceIndex(int)+addRipInterface():String+editRipInterface():String+deleteRipInterface():String
«Cobia Entity»
RipInterface{from Router domain package}
+getInterface():EthInterface+setInterface(EthInterface)+isEnabled():boolean+setEnabled(boolean)+getMetric():int+setMetric(int)EntitySetRenderer
retrieves the collectionfrom the backing beanusing a value binding
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 58
Colorado Software Summit: October 21 – 26, 2007
EntitySet: component design
UIEntitySet
+setValue(String):void+getColumns():List
EntitySetTag
+setValue(String)+setVar(String)+setAllowsAdd(boolean)+setAllowsEdit(boolean)+setAllowsDelete(boolean)#setProperties(UIComponent)
HtmlInputHidden{j.f.components.html}
-VB:#{ripMgr.ripInterfaceIndex}
children
ColumnTag
+setValue(String)+setType(String)+setSort(boolean)#setProperties(UIComponent)
UIColumn
-VB=#{rip.interface}
HtmlCommandLink{j.f.components.html}
-MB:#{ripMgr.addRipInterface}
HtmlCommandLink{j.f.components.html}
-MB:#{ripMgr.editRipInterface}
...
...
UIColumn
-VB=#{rip.enabled} ColumnTag
...
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 59
Colorado Software Summit: October 21 – 26, 2007
EntitySet: Rendered template
<div class="addItemTable"> <ul class="addItem"> <li> <a href="#" onclick="return oamSubmitForm(...);" id="x:ripInterfacesTable-addLink">
add a rip interface </a> </li> </ul></div><input id="x:ripInterfacesTableIndex" name="x:ripInterfacesTableIndex" value="0" type="hidden"><a href="#" onclick="return oamSubmitForm(...);" id="x:ripInterfacesTable-editLink"></a><script language="javascript" type="text/javascript"> function ripInterfacesTable_editAction(index) { document.getElementById('x:ripInterfacesTableIndex').value = index; return document.getElementById('x:ripInterfacesTable-editLink').onclick(); }</script><a href="#" onclick="return oamSubmitForm(...);" id="x:ripInterfacesTable-deleteLink"></a><script language="javascript" type="text/javascript"> function ripInterfacesTable_deleteAction(index) { document.getElementById('x:ripInterfacesTableIndex').value = index; return document.getElementById('x:ripInterfacesTable-deleteLink').onclick(); }</script><!-- more code on next slide -->
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 60
Colorado Software Summit: October 21 – 26, 2007
EntitySet: Rendered template
<!-- continued code from previous slide --><div class="dataTableContainerScrollable" id="ripInterfacesTable"> <table cellpadding="0" cellspacing="0" border="0"> <thead id="ripInterfacesTableHead"> <tr> <th class="dataTableHeaderString dataTableHeaderFirst" id="interface">interface</th> <th class="dataTableHeaderString" id="interfaceStatus">status</th>
<!-- more column TH cells --> <th class="dataTableHeaderAction" id="edit"> </th> <th class="dataTableHeaderAction dataTableHeaderLast" id="delete"> </th>
</tr> </thead> <tbody id="ripInterfacesTableBody"> <tr> <th class="dataTableString dataTableCellFirst" headers="interface">eth0</th>
<td class="dataTableString" headers="interfaceStatus">up</td> <!-- more column TD cells --> <td class="dataTableAction" headers="edit"> <a href="#" onclick="return ripInterfacesTable_editAction(0);">edit</a> </td> <td class="dataTableAction dataTableCellLast" headers="delete"> <a href="#" onclick="return ripInterfacesTable_deleteAction(0);">edit</a> </td>
</tr> <!-- more entity rows --> </tbody> </table></div>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 61
Colorado Software Summit: October 21 – 26, 2007
EntitySet: Backing bean
public class ConfigureRIP extends AbstractUpdateContent{ // UIEntitySet component id='ripInterfacesTable' private Set<RipInterface> ripInterfacesSet; public void setRipInterfacesSet(Set<RipInterface> ripInterfaces) { ripInterfacesSet = ripInterfaces; } private List<RipInterface> ripInterfaces; public List<RipInterface> getRipInterfaces() { if ( ripInterfaces == null ) { ripInterfaces = new ArrayList<RipInterface>(ripInterfacesSet.size()); for ( RipInterface item : ripInterfacesSet ) { ripInterfaces.add(item); }
// PERFORM SORT (NOT SHOWN) } return ripInterfaces; } protected void clearRipInterfaces() { ripInterfaces = null; }// more code on next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 62
Colorado Software Summit: October 21 – 26, 2007
EntitySet: Backing bean
// code continued from previous slide
/** * Implements the "add a rip interface" action method. */ public String addRipInterface() { // Create the new RIP interface and pass to screen RipInterface ripInterface = new RipInterface(); ripInterface.setState(Entity.State.ADDED); AddRipInterface addScreen = new AddRipInterface(this, ripInterface); return addScreen.open(); } /** * Adds a new RIP interface to the set. This is used by the AddRipInterface * screen backing bean. */ public void addRipInterface(RipInterface addedRip) { ripInterfacesSet.add(addedRip); clearRipInterfaces(); }
// more code on next slide
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 63
Colorado Software Summit: October 21 – 26, 2007
EntitySet: Backing bean
// code continued from previous slide
/** Maps to the hidden input with VB=#{ripMgr.ripInterfacesIndex} */ private int ripInterfacesIndex; public int getRipInterfacesIndex() { return ripInterfacesIndex; } public void setRipInterfacesIndex(int index) { ripInterfacesIndex = index; } protected RipInterface getRipInterface() { return ripInterfaces.get(ripInterfacesIndex); } public String editRipInterface() { EditRipInterface editScreen = new EditRipInterface(this, getRipInterface()); return editScreen.open(); } // more code in backing bean
} // END of ConfigureRIP class
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 64
Colorado Software Summit: October 21 – 26, 2007
Topics Mind Map
Reviewof JSF
CustomComponents
DataConversion
DataValidation
ScreenManagement
ExtendingJSF
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 65
Colorado Software Summit: October 21 – 26, 2007
Simplify the HTML/JSP
UIScreen component: styles and scripts
Boundary components: JSP and backing
bean
Manage breadcrumbs
Manage conversation state
Screen Mgt: requirements
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 66
Colorado Software Summit: October 21 – 26, 2007
DEMO: UI Design is rich
GeneralSettings: 196 lines (14)
ConfigureRIP: 299 lines (103)
EditFirewallRule: 583 lines (416)
Simplify HTML
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 67
Colorado Software Summit: October 21 – 26, 2007
Phase one uses:
raw HTML
standard JSP component
Tiles framework from Struts
GeneralSettings: 43 lines (21)
ConfigureRIP: 88 lines (67)
EditFirewallRule: 430 lines (385)
Simplify HTML: phase one
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 68
Colorado Software Summit: October 21 – 26, 2007
Phase two uses Cobia UI components
GeneralSettings: 52 lines (7)
ConfigureRIP: 63 lines (18)
EditFirewallRule: 83 lines (35)
Simplify HTML: phase two
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 69
Colorado Software Summit: October 21 – 26, 2007
UIScreen is the “root” component
Renders the DOCTYPE, <html>, <head>,
and <body> tags
Includes necessary JavaScript files
Includes necessary CSS files
... well, OK, it's not the JSF root
UIScreen
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 70
Colorado Software Summit: October 21 – 26, 2007
UIScreen: JSPX template
<jsp:root xmlns:jsp='http://java.sun.com/JSP/Page' version='2.0'xmlns:f='http://java.sun.com/jsf/core'xmlns:h='http://java.sun.com/jsf/html'xmlns:c='http://cobia.stillsecure.org/jsf'>
<jsp:directive.page contentType='text/html; charset=UTF-8' /><f:view><c:screen value='#{BEAN-NAME}'><h:form id='x'>
<c:division id='header'> <!-- LOGO / APPLICATION NAV --> </c:division> <c:division id='container'> <!-- MAIN PAGE CONTENT --> </c:division> <c:division id='footer'> <!-- COPYRIGHT / VERSION / LICENSE --> </c:division> </h:form></c:screen></f:view></jsp:root>
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 71
Colorado Software Summit: October 21 – 26, 2007
UIScreen: component tree
UIViewRoot
UIScreen
UIForm
UIDivision UIDivision UIDivision
UIModNav UIBcTrail UIFrame
UIScrMsgs UIScrButtons UIFullFrame
UIDrpDwnList UIBoolChkbox... UIImageList...
CSS CSSCSS
CSS
CSS
JSJSJS
JS
CSS CSS CSS
CSSCSS
JS
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 72
Colorado Software Summit: October 21 – 26, 2007
Jacobson's analysis model
Boundary (UI)
Service (aka Control)
Entity (aka Domain object)
Boundary components
Boundary
Service
Entity
EditRipInterface
RipInterface
XorpManager
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 73
Colorado Software Summit: October 21 – 26, 2007
Boundary components
EditRipInterface.class
<jsp> ... ....</jsp>
EditRipInterface.jspx
EditRipInterface
Boundary == View + Controller
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 74
Colorado Software Summit: October 21 – 26, 2007
Screen backing beans
<jsp> ... ....</jsp>
EditRipInterface.jspx
UIScreen
+getScreen():Screen+encodeBegin
«enumeratedType»
ScreenType
+DASHBOARD+MULTI_PAGE+FULL_SCREEN
«interface»
Screen
+getType():ScreenType+getViewId():String+open():String+refresh():String+free():void
«interface»
UpdateScreen
+isModified():boolean+save():void
AbstractScreen
{abstract}
AbsUpdateScreen
{abstract}
EditRipInterface{from Router module}
<c:screen value=#{ripMgr}>
ripMgr is stored in session-scope
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 75
Colorado Software Summit: October 21 – 26, 2007
Screen hierarchies
Dashboard
ConfigureRIP
ConfigureBGP
ConfigureStaticRoutes
AddRipInterface
EditRipInterface
EditBgpPeer
EditStaticRoute
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 76
Colorado Software Summit: October 21 – 26, 2007
Breadcrumb trail
UI view of breadcrumb trail
«screen»
Dashboard«screen»
ConfigureRIP«screen»
EditRipInterfaceparent parent
Screen parent relationship
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 77
Colorado Software Summit: October 21 – 26, 2007
Manage UI conversation
ScreenBean manages saving or canceling screen state
UpdateScreen buttons
«screen»
Dashboard«screen»
ConfigureRIP«screen»
EditRipInterfaceparent parent
«backingBean»
ScreenBean
if isModified,then save
pop toparent
© Copyright 2007, StillSecure, Inc.
Bryan Basham: Extending JSF to Build a Product-Specific UI Framework Slide 78
Colorado Software Summit: October 21 – 26, 2007
Topics Mind Map
Reviewof JSF
CustomComponents
DataConversion
DataValidation
ScreenManagement
ExtendingJSF