Moved Spring Web Flow to a top level project

This commit is contained in:
Ben Hale
2006-12-21 16:02:27 +00:00
commit e7ecbbc7e5
1083 changed files with 80775 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# $Header$
# Contains filterable project settings. Setting placeholders in filterable project text
# files will be replaced with these values when the 'statics' build target is run.
#
# You may add static settings directly to this source file in the format:
# setting=value e.g MY_SETTING=myvalue
# This is appropriate usage if you know the setting value will never change.
#
# At build time this source file is copied to the ${target.dir} where additional
# dynamic settings may be appended using the <propertyfile> task. Use this approach
# when a setting value depends on the build or the local user's environment.
#
# An example of this approach is shown below:
#
# build.xml
# <target name="build.prepare" depends="common-targets.build.prepare">
# <!-- Append additional local settings that are applicable to this project -->
# <propertyfile file="${target.filter.file}">
# <!-- key=the name of the setting
# value=the property in your build.properties file that has the local setting value -->
# <entry key="MY_LOCAL_SETTING" value="${my.local.setting}" />
# </propertyfile>
# </target>
#
# This allows for dynamic replacement values that are sourced from local properties files to facilitate
# local user settings.
#
# To refer to filterable settings within project source files like config files, JSPs, or
# other text files use the standard ant placeholder format:
# @SETTING_NAME@ e.g, @MY_SETTING@ and @MY_LOCAL_SETTING@
#
# Your settings:

View File

@@ -0,0 +1,20 @@
log4j.rootCategory=WARN, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=${@PROJECT_WEBAPP_NAME@.root}/@PROJECT_WEBAPP_NAME@.log
log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files
log4j.appender.logfile.MaxBackupIndex=3
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
#Pattern to output : date priority [category] - <message>line_separator
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - <%m>%n
#Enable webflow debug logging
log4j.category.org.springframework.webflow=DEBUG
log4j.category.org.springframework.binding=DEBUG

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.samples.sellitem;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class InMemoryDatabaseCreator extends JdbcDaoSupport {
@Override
protected void initDao() throws Exception {
String createSales = "create table T_SALES (ID int not null identity primary key, ITEM_COUNT int not null, PRICE double NOT NULL, category VARCHAR(1) NOT NULL, SHIPPING_TYPE varchar(1))";
getJdbcTemplate().execute(createSales);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.samples.sellitem;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class JdbcSaleProcessor extends JdbcDaoSupport implements SaleProcessor {
public void process(Sale sale) {
getJdbcTemplate()
.update("insert into T_SALES values (?, ?, ?, ?, ?)",
new Object[] { null, sale.getPrice(), sale.getItemCount(), sale.getCategory(),
sale.getShippingType() });
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.samples.sellitem;
import java.io.Serializable;
import org.springframework.core.style.ToStringCreator;
public class Sale implements Serializable {
private double price;
private int itemCount;
private String category;
private boolean shipping;
private String shippingType;
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public int getItemCount() {
return itemCount;
}
public void setItemCount(int itemCount) {
this.itemCount = itemCount;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isShipping() {
return shipping;
}
public void setShipping(boolean shipping) {
this.shipping = shipping;
}
public String getShippingType() {
return shippingType;
}
public void setShippingType(String shippingType) {
this.shippingType = shippingType;
}
// business logic methods
/**
* Returns the base amount of the sale, without discount or delivery costs.
*/
public double getAmount() {
return price * itemCount;
}
/**
* Returns the discount rate to apply.
*/
public double getDiscountRate() {
double discount = 0.02;
if ("A".equals(category)) {
if (itemCount >= 100) {
discount = 0.1;
}
}
else if ("B".equals(category)) {
if (itemCount >= 200) {
discount = 0.2;
}
}
return discount;
}
/**
* Returns the savings because of the discount.
*/
public double getSavings() {
return getDiscountRate() * getAmount();
}
/**
* Returns the delivery cost.
*/
public double getDeliveryCost() {
double delCost = 0.0;
if ("S".equals(shippingType)) {
delCost = 10.0;
}
else if ("E".equals(shippingType)) {
delCost = 20.0;
}
return delCost;
}
/**
* Returns the total cost of the sale, including discount and delivery cost.
*/
public double getTotalCost() {
return getAmount() + getDeliveryCost() - getSavings();
}
public String toString() {
return new ToStringCreator(this).append("price", price).append("itemCount", itemCount).toString();
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.samples.sellitem;
import org.springframework.transaction.annotation.Transactional;
@Transactional
public interface SaleProcessor {
public void process(Sale sale);
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.samples.sellitem;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class SaleValidator implements Validator {
public boolean supports(Class clazz) {
return Sale.class.equals(clazz);
}
public void validate(Object obj, Errors errors) {
Sale sale = (Sale)obj;
validatePriceAndItemCount(sale, errors);
}
public void validatePriceAndItemCount(Sale sale, Errors errors) {
// the next two items are normallhy more appropriately handled by JSF
// field
// validation. We'll leave them here for safety
if (sale.getItemCount() <= 0) {
errors.rejectValue("itemCount", "tooLittle", "Item count must be greater than 0");
}
if (sale.getPrice() <= 0.0) {
errors.rejectValue("price", "tooLittle", "Price must be greater than 0.0");
}
// perhaps an artificial example, but we want to show that in the JSF
// integration
// validators are best used for validation of field relationships.
// Individual fields
// are better validated with simple JSF field validation
if (sale.getItemCount() * sale.getPrice() > 1000000)
errors.reject("saleTooLarge", "total dollar value for sale above allowed limit");
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.samples.sellitem;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
public class SellItemAction extends AbstractAction {
// this does nothing. We're just showing how in the JSF integration, while
// an action is not needed for binding, you can still add in an action to do
// something if you need to
protected Event doExecute(RequestContext context) throws Exception {
Sale sale = (Sale)context.getFlowScope().getRequired("sale", Sale.class);
sale.getAmount();
return success();
}
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="saleProcessor" parent="transactionProxy">
<property name="target">
<bean class="org.springframework.webflow.samples.sellitem.JdbcSaleProcessor" autowire="byType" />
</property>
</bean>
<bean id="transactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributeSource">
<bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:sellItem" />
<property name="username" value="sa" />
</bean>
<bean id="databaseCreator" class="org.springframework.webflow.samples.sellitem.InMemoryDatabaseCreator"
autowire="byType" />
</beans>

View File

@@ -0,0 +1,9 @@
log4j.rootCategory=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
# Enable web flow logging
log4j.category.org.springframework.webflow=DEBUG
log4j.category.org.springframework.binding=DEBUG

View File

@@ -0,0 +1,33 @@
<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
<application>
<!-- Navigation handler proxy for a Spring-managed bean that is the Web Flow Navigation Handler -->
<navigation-handler>
org.springframework.web.jsf.DelegatingNavigationHandlerProxy
</navigation-handler>
<property-resolver>
org.springframework.webflow.executor.jsf.FlowPropertyResolver
</property-resolver>
<variable-resolver>
org.springframework.webflow.executor.jsf.FlowVariableResolver
</variable-resolver>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
<!-- Extended "webApplicationContext" resolver -->
<variable-resolver>
org.springframework.web.jsf.WebApplicationContextVariableResolver
</variable-resolver>
</application>
<lifecycle>
<!-- Multi-caster that broadcast phase events to all PhaseListeners managed by Spring -->
<phase-listener>org.springframework.web.jsf.DelegatingPhaseListenerMulticaster</phase-listener>
</lifecycle>
</faces-config>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">
<start-state idref="enterPriceAndItemCount" />
<view-state id="enterPriceAndItemCount" view="/priceAndItemCountForm.jsp">
<transition on="submit" to="enterCategory">
<action bean="sellItemFormAction" method="validate">
<attribute name="validatorMethod" value="validatePriceAndItemCount" />
</action>
</transition>
</view-state>
<view-state id="enterCategory" view="/categoryForm.jsp">
<transition on="submit" to="requiresShipping" />
</view-state>
<decision-state id="requiresShipping">
<if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale" />
</decision-state>
<view-state id="enterShippingDetails" view="/shippingDetailsForm.jsp">
<transition on="submit" to="processSale" />
</view-state>
<action-state id="processSale">
<bean-action bean="saleProcessor" method="process">
<method-arguments>
<argument expression="flowScope.sale" />
</method-arguments>
</bean-action>
<transition on="success" to="showCostOverview" />
</action-state>
<end-state id="showCostOverview" view="/costOverview" />
</flow>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app 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-app_2_4.xsd"
version="2.4">
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>swf-sellitem-jsf.root</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:org/springframework/webflow/samples/sellitem/services-config.xml
/WEB-INF/webflow-config.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- a Listener for MyFaces, that does all the startup work (configuration, init). -->
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<!-- Faces Servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd">
<!-- Creates the registry of flow definitions for this application -->
<flow:registry id="flowDefinitionLocator">
<flow:location path="/WEB-INF/flows/sellitem-flow.xml" />
</flow:registry>
<!-- A "Sale" managed bean created by JSF for each 'sellitem' flow execution -->
<bean name="sale" class="org.springframework.webflow.samples.sellitem.Sale" scope="prototype" />
<!-- A form action for applying custom validation to the Sale managed bean -->
<bean id="sellItemFormAction" class="org.springframework.webflow.action.FormAction">
<property name="formObjectName" value="sale" />
<property name="formObjectClass" value="org.springframework.webflow.samples.sellitem.Sale" />
<property name="formObjectScope" value="FLOW" />
<property name="validator">
<bean class="org.springframework.webflow.samples.sellitem.SaleValidator" />
</property>
</bean>
<!--
Spring configured flow navigation handler delegate, allowing for custom configuration
using standard dependency injection techniques.
Note: this definition is optional; you may choose to simply specify your FlowNavigationHandler
in your faces-config.xml if its defaults meet your needs.
-->
<bean id="jsfNavigationHandler" class="org.springframework.webflow.executor.jsf.FlowNavigationHandler" />
<!--
Spring configured flow phase listener delegate, allowing for custom configuration using
standard dependency injection techniques.
Note: this definition is optional; you may choose to simply specify your FlowPhaseListener
in your faces-config.xml if its defaults meet your needs.
-->
<bean id="flowPhaseListener" class="org.springframework.webflow.executor.jsf.FlowPhaseListener" />
</beans>

View File

@@ -0,0 +1,45 @@
<%@ include file="includeTop.jsp" %>
<f:view>
<div id="content">
<div id="insert"><img src="images/webflow-logo.jpg"/></div>
<h2>Select category</h2>
<table>
<tr class="readOnly">
<td>Price:</td><td><h:outputText value="#{flowScope.sale.price}"/></td>
</tr>
<tr class="readOnly">
<td>Item count:</td><td><h:outputText value="#{flowScope.sale.itemCount}"/></td>
</tr>
<h:form id="categoryForm">
<tr>
<td>Category:</td>
<td>
<h:selectOneMenu value="#{flowScope.sale.category}">
<f:selectItem itemLabel="None (0.02 discount rate)" itemValue=""/>
<f:selectItem itemLabel="Cat. A (0.1 discount rate when more than 100 items)" itemValue="A"/>
<f:selectItem itemLabel="Cat. B (0.2 discount rate when more than 200 items)" itemValue="B"/>
</h:selectOneMenu>
</td>
</tr>
<tr>
<td>Is shipping required?:</td>
<td>
<h:selectBooleanCheckbox value="#{flowScope.sale.shipping}"/>
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"/>
<h:commandButton type="submit" value="Next" action="submit" immediate="false" /></td>
</td>
</tr>
</h:form>
</table>
</div>
</f:view>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,59 @@
<%@ include file="includeTop.jsp" %>
<f:view>
<div id="content">
<div id="insert"><img src="images/webflow-logo.jpg"/></div>
<h2>Purchase cost overview</h2>
<hr>
<table>
<tr class="readOnly">
<td>Price:</td><td>${sale.price}</td>
</tr>
<tr class="readOnly">
<td>Item count:</td><td>${sale.itemCount}</td>
</tr>
<tr class="readOnly">
<td>Category:</td><td>${sale.category}</td>
<tr class="readOnly">
<td>Shipping:</td>
<c:choose>
<c:when test="${sale.shipping}">
<td>${sale.shippingType}</td>
</c:when>
<c:otherwise>
<td>No shipping required: you're picking up the items</td>
</c:otherwise>
</c:choose>
</tr>
<tr>
<td colspan="2"></td>
</tr>
<tr>
<td>Base amount:</td><td>${sale.amount}</td>
</tr>
<tr>
<td>Delivery cost:</td><td>${sale.deliveryCost}</td>
</tr>
<tr>
<td>Discount:</td><td>${sale.savings} (Discount rate: ${sale.discountRate})</td>
</tr>
<tr>
<td colspan="2"><hr></td>
</tr>
<tr>
<td><b>Total cost</b>:</td><td>${sale.totalCost}</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<form action="<c:url value="/index.jsf"/>">
<input type="submit" class="button" value="Home">
</form>
</td>
</tr>
</table>
</div>
</f:view>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,17 @@
<%@ include file="includeTop.jsp" %>
<div id="content">
<div id="insert">
<img src="images/webflow-logo.jpg"/>
</div>
<p>
<span class="error">
Duplicate submit of the same transaction not allowed!
</span>
</p>
<p>
<A href="pos.htm?_flowId=sellitem">Sell a new item</A>
</p>
</div>
<%@ include file="includeBottom.jsp" %>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,5 @@
<div id="copyright">
<p>&copy; Copyright 2002-2006, <a href="http://www.springframework.org">www.springframework.org</a>, under the terms of the Apache 2.0 software license.</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,25 @@
<%@ page contentType="text/html" %>
<%@ page session="false" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>Sell an item</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<div id="logo">
<img src="images/spring-logo.jpg" alt="Logo" border="0">
</div>
<div id="navigation">
</div>

View File

@@ -0,0 +1,10 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
</head>
<body>
<jsp:forward page="intro.jsf" />
</body>
</html>

View File

@@ -0,0 +1,81 @@
<%@ page session="true" %> <%-- make sure we have a session --%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<HTML>
<BODY>
<f:view>
<DIV align="left">Sell Item - A Spring Web Flow Sample (JSF Version)</DIV>
<HR>
<DIV align="left">
<P>
<h:form>
<h:commandLink value="Sell Item" action="flowId:sellitem-flow"/>
</h:form>
</P>
<P>
This Spring web flow sample application implements the example application
discussed in the article
<A href="http://www-128.ibm.com/developerworks/java/library/j-contin.html">
Use continuations to develop complex Web applications</A>. It illustrates
the following concepts:
<UL>
<LI>
Spring Web Flow's JSF integration.
</LI>
<LI>
Using the flowId: command link prefix to let the view tell the web
flow controller which flow needs to be started.
</LI>
<LI>
Implementing a wizard using web flows.
</LI>
<LI>
Using <A href="http://www.ognl.org/">OGNL</A> based conditional expressions.
</LI>
</UL>
<UL>
<LI>
Note on continuations: The original sellitem sample shows
continuations in use, in the words of the intro, "Using
continuations to make the flow completely stable, no matter
how browser navigation buttons are used."<br/>
This JSF version of sellitem is currently set to use normal
session storage.<br/>
The JSF Web Flow integration does support the continuation storages.
However, because JSF page components themselves have internal state,
it is not enough for Web Flow to be using continuation storage, the
JSF engine itself must be configured to use client-side or server-side
continuation style storage for the component state, instead of the
normal shared, Session based storage. We have not yet investigated
how to set MyFaces or the JSR RI to use client side storage, but
it is theoretically possible at least to the extent that the JSF
specification talks about JSF implementations offering it as an
<em>option</em>. We have seen no discussion of server-side continuation-
style storage for JSF component state<br/>
If you do configure your JSF engine for client-side storage of
component state, and set Web Flow to use client side continuation
storage it does mean that pages can not trigger the auto-creation of
flow variables on demand since the flow execution id needs to be known
to the page, and the id _is_ the storage for the flow state, a classic
chicken and egg situation. Just make sure any flow-scoped variables
are created ahead of time in the flow, before any JSF page component
tried to reference them.
</LI>
</UL>
</P>
</DIV>
<HR>
<DIV align="right"></DIV>
</BODY>
</f:view>
</HTML>

View File

@@ -0,0 +1,54 @@
<%@ page contentType="text/html" %>
<%@ include file="includeTop.jsp"%>
<f:view>
<div id="content">
<div id="insert"><img src="images/webflow-logo.jpg" /></div>
<!-- display any errors from sale Validator -->
<c:if test="${not empty sale}">
<spring:bind path="sale.*">
<c:forEach items="${status.errorMessages}" var="curError">
<div class="error">${curError}</div>
</c:forEach>
</spring:bind>
</c:if>
<h2>Enter price and item count</h2>
<hr>
<table>
<h:form id="priceAndItemCountForm">
<tr>
<td>Price:</td>
<td><h:inputText id="price" value="#{flowScope.sale.price}"
required="true">
<f:validateDoubleRange minimum="0.01"/>
</h:inputText>
&nbsp;&nbsp;
<h:message for="price" style="color: red"/>
</td>
</tr>
<tr>
<td>Item count:</td>
<td><h:inputText id="itemCount" value="#{flowScope.sale.itemCount}"
required="true">
<f:validateLongRange minimum="1"/>
</h:inputText>
&nbsp;&nbsp;
<h:message for="itemCount" style="color: red"/>
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"/>
<h:commandButton type="submit" value="Next" action="submit" immediate="false" /></td>
</tr>
</h:form>
</table>
</div>
</f:view>
<%@ include file="includeBottom.jsp"%>

View File

@@ -0,0 +1,44 @@
<%@ include file="includeTop.jsp" %>
<f:view>
<div id="content">
<div id="insert"><img src="images/webflow-logo.jpg"/></div>
<h2>Enter shipping information</h2>
<hr>
<table>
<tr class="readOnly">
<td>Price:</td><td><h:outputText value="#{flowScope.sale.price}"/></td>
</tr>
<tr class="readOnly">
<td>Item count:</td><td><h:outputText value="#{flowScope.sale.itemCount}"/></td>
</tr>
<tr class="readOnly">
<td>Category:</td><td><h:outputText value="#{flowScope.sale.category}"/></td>
<tr class="readOnly">
<td>Shipping:</td><td><h:outputText value="#{flowScope.sale.shipping}"/></td>
</tr>
<h:form id="shippingForm">
<tr>
<td>Shipping type:</td>
<td>
<h:selectOneMenu value="#{flowScope.sale.shippingType}">
<f:selectItem itemLabel="Standard (10 extra cost)" itemValue="S"/>
<f:selectItem itemLabel="Express (20 extra cost)" itemValue="E"/>
</h:selectOneMenu>
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"/>
<h:commandButton type="submit" value="Next" action="submit" immediate="false" /></td>
</td>
</tr>
</h:form>
</table>
</div>
</f:view>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,58 @@
body {
width: 720px;
margin: 0px;
padding: 0px;
}
div#logo {
width: 720px;
height: 73px;
background: #86AEA5;
}
div#navigation {
width: 720px;
height: 15px;
background: #E2F3B8;
text-align: right;
}
div#content {
width: 720px;
padding: 5px;
}
div#insert {
width: 120;
float: right;
text-align: right;
}
.buttonBar {
height: 1.5em;
text-align: right;
}
div#copyright {
width: 720px;
}
div#copyright p {
text-align: center;
font-family: Tahoma, sans-serif;
font-size: 75%;
color: div#336633;
margin-left: 5px;
font-weight: bold;
clear: both;
}
.readOnly {
color: rgb(192, 192, 192);
}
.error {
color: red;
font-weight: bold;
font-family: Arial, sans-serif;
}

View File

@@ -0,0 +1,9 @@
log4j.rootCategory=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
# Enable web flow logging
log4j.category.org.springframework.webflow=DEBUG
log4j.category.org.springframework.binding=DEBUG

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.samples.sellitem;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
public class SaleProcessorIntegrationTests extends AbstractTransactionalDataSourceSpringContextTests {
private SaleProcessor saleProcessor;
public void setSaleProcessor(SaleProcessor saleProcessor) {
this.saleProcessor = saleProcessor;
}
@Override
protected String[] getConfigLocations() {
return new String[] { "classpath:org/springframework/webflow/samples/sellitem/services-config.xml" };
}
public void testProcessSale() {
int beforeCount = jdbcTemplate.queryForInt("select count(*) from T_SALES");
Sale sale = new Sale();
sale.setItemCount(25);
sale.setPrice(100.0);
sale.setCategory("A");
sale.setShippingType("Express");
saleProcessor.process(sale);
int afterCount = jdbcTemplate.queryForInt("select count(*) from T_SALES");
assertEquals("Wrong after count", beforeCount + 1, afterCount);
}
}