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,25 @@
/*
* 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,144 @@
/*
* 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 java.util.Date;
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;
private Date shipDate;
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;
}
public Date getShipDate() {
return shipDate;
}
public void setShipDate(Date shipDate) {
this.shipDate = shipDate;
}
// 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).append("shippingType",
shippingType).append("shipDate", shipDate).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,40 @@
/*
* 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) {
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");
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import org.springframework.webflow.context.servlet.ServletExternalContext;
import org.springframework.webflow.definition.StateDefinition;
import org.springframework.webflow.execution.EnterStateVetoException;
import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
import org.springframework.webflow.execution.RequestContext;
public class SellItemFlowExecutionListener extends FlowExecutionListenerAdapter {
public void stateEntering(RequestContext context, StateDefinition nextState) throws EnterStateVetoException {
String role = nextState.getAttributes().getString("role");
if (StringUtils.hasText(role)) {
HttpServletRequest request = ((ServletExternalContext)context.getExternalContext()).getRequest();
if (!request.isUserInRole(role)) {
throw new EnterStateVetoException(context.getActiveFlow().getId(), context.getCurrentState().getId(),
nextState.getId(), "State requires role '" + role
+ "', but the authenticated user doesn't have it!");
}
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.propertyeditors.CustomDateEditor;
public class SellItemPropertyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true));
}
}

View File

@@ -0,0 +1,28 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="saleProcessor" class="org.springframework.webflow.samples.sellitem.JdbcSaleProcessor">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven/>
<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,20 @@
<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.xsd">
<!-- Manages setting up, binding input to, and validating a Sale "backing wizard form object" -->
<bean id="formAction" class="org.springframework.webflow.action.FormAction">
<property name="formObjectClass" value="org.springframework.webflow.samples.sellitem.Sale"/>
<property name="formObjectScope" value="CONVERSATION"/>
<property name="formErrorsScope" value="CONVERSATION"/>
<property name="validator">
<bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/>
</property>
<!-- Installs property editors used to format non-String fields like 'shipDate' -->
<property name="propertyEditorRegistrar">
<bean class="org.springframework.webflow.samples.sellitem.SellItemPropertyEditorRegistrar"/>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,54 @@
<?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-actions>
<!-- create the backing form object and initialize a empty errors collection -->
<action bean="formAction" method="setupForm"/>
</start-actions>
<start-state idref="enterPriceAndItemCount"/>
<view-state id="enterPriceAndItemCount" view="priceAndItemCountForm">
<transition on="submit" to="enterCategory">
<action bean="formAction" method="bindAndValidate">
<attribute name="validatorMethod" value="validatePriceAndItemCount"/>
</action>
</transition>
</view-state>
<view-state id="enterCategory" view="categoryForm">
<transition on="submit" to="requiresShipping">
<action bean="formAction" method="bind"/>
</transition>
</view-state>
<decision-state id="requiresShipping">
<if test="${conversationScope.sale.shipping}" then="enterShippingDetails" else="processSale"/>
</decision-state>
<subflow-state id="enterShippingDetails" flow="shipping-conversation-scope-flow">
<transition on="finish" to="processSale"/>
</subflow-state>
<action-state id="processSale">
<bean-action bean="saleProcessor" method="process">
<method-arguments>
<argument expression="conversationScope.sale"/>
</method-arguments>
</bean-action>
<transition on="success" to="finish"/>
</action-state>
<end-state id="finish" view="costOverview">
<entry-actions>
<!-- force reinstall of property editors so costOverview can render formatted Sale values -->
<action bean="formAction" method="setupForm"/>
</entry-actions>
</end-state>
<import resource="sellitem-beans.xml"/>
</flow>

View File

@@ -0,0 +1,19 @@
<?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="enterShippingDetails"/>
<view-state id="enterShippingDetails" view="shippingDetailsForm">
<transition on="submit" to="finish">
<action bean="formAction" method="bind"/>
</transition>
</view-state>
<end-state id="finish"/>
<import resource="sellitem-beans.xml"/>
</flow>

View File

@@ -0,0 +1,18 @@
<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">
<!-- Manages setting up, binding input to, and validating a Sale "backing wizard form object" -->
<bean id="formAction" class="org.springframework.webflow.action.FormAction">
<property name="formObjectClass" value="org.springframework.webflow.samples.sellitem.Sale"/>
<property name="validator">
<bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/>
</property>
<!-- Installs property editors used to format non-String fields like 'shipDate' -->
<property name="propertyEditorRegistrar">
<bean class="org.springframework.webflow.samples.sellitem.SellItemPropertyEditorRegistrar"/>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,59 @@
<?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-actions>
<!-- create the backing form object and initialize a empty errors collection -->
<action bean="formAction" method="setupForm"/>
</start-actions>
<start-state idref="enterPriceAndItemCount"/>
<view-state id="enterPriceAndItemCount" view="priceAndItemCountForm">
<transition on="submit" to="enterCategory">
<action bean="formAction" method="bindAndValidate">
<attribute name="validatorMethod" value="validatePriceAndItemCount"/>
</action>
</transition>
</view-state>
<view-state id="enterCategory" view="categoryForm">
<transition on="submit" to="requiresShipping">
<action bean="formAction" method="bind"/>
</transition>
</view-state>
<decision-state id="requiresShipping">
<if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/>
</decision-state>
<subflow-state id="enterShippingDetails" flow="shipping-flow">
<attribute-mapper>
<input-mapper>
<input-attribute name="sale"/>
</input-mapper>
</attribute-mapper>
<transition on="finish" to="processSale"/>
</subflow-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="finish"/>
</action-state>
<end-state id="finish" view="costOverview">
<entry-actions>
<!-- force reinstall of property editors so costOverview can render formatted Sale values -->
<action bean="formAction" method="setupForm"/>
</entry-actions>
</end-state>
<import resource="sellitem-beans.xml"/>
</flow>

View File

@@ -0,0 +1,23 @@
<?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">
<input-mapper>
<input-attribute name="sale"/>
</input-mapper>
<start-state idref="enterShippingDetails"/>
<view-state id="enterShippingDetails" view="shippingDetailsForm">
<transition on="submit" to="finish">
<action bean="formAction" method="bind"/>
</transition>
</view-state>
<end-state id="finish"/>
<import resource="sellitem-beans.xml"/>
</flow>

View File

@@ -0,0 +1,56 @@
<?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-actions>
<!-- create the backing form object and initialize a empty errors collection -->
<action bean="formAction" method="setupForm"/>
</start-actions>
<start-state idref="enterPriceAndItemCount"/>
<view-state id="enterPriceAndItemCount" view="priceAndItemCountForm">
<transition on="submit" to="enterCategory">
<action bean="formAction" method="bindAndValidate">
<attribute name="validatorMethod" value="validatePriceAndItemCount"/>
</action>
</transition>
</view-state>
<view-state id="enterCategory" view="categoryForm">
<transition on="submit" to="requiresShipping">
<action bean="formAction" method="bind"/>
</transition>
</view-state>
<decision-state id="requiresShipping">
<if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/>
</decision-state>
<view-state id="enterShippingDetails" view="shippingDetailsForm">
<transition on="submit" to="processSale">
<action bean="formAction" method="bind"/>
</transition>
</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="finish"/>
</action-state>
<end-state id="finish" view="costOverview">
<entry-actions>
<!-- force reinstall of property editors so costOverview can render formatted Sale values -->
<action bean="formAction" method="setupForm"/>
</entry-actions>
</end-state>
<import resource="../sellitem-beans.xml"/>
</flow>

View File

@@ -0,0 +1,48 @@
<%@ include file="includeTop.jsp" %>
<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>${sale.price}</td>
</tr>
<tr class="readOnly">
<td>Item count:</td><td>${sale.itemCount}</td>
</tr>
<form:form commandName="sale" method="post">
<tr>
<td>Category:</td>
<td>
<spring:bind path="sale.category">
<select name="${status.expression}">
<option value="" <c:if test="${status.value ==''}">selected</c:if>>
None (0.02 discount rate)
</option>
<option value="A" <c:if test="${status.value =='A'}">selected</c:if>>
Cat. A (0.1 discount rate when more than 100 items)
</option>
<option value="B" <c:if test="${status.value =='B'}">selected</c:if>>
Cat. B (0.2 discount rate when more than 200 items)
</option>
</select>
</spring:bind>
</td>
</tr>
<tr>
<td>Is shipping required?:</td>
<td>
<form:checkbox path="shipping"/>
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
<input type="submit" class="button" name="_eventId_submit" value="Next">
</td>
</tr>
</form:form>
</table>
</div>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,71 @@
<%@ include file="includeTop.jsp" %>
<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>
<tr class="readOnly">
<td valign="top">Shipping Info:</td>
<td>
<c:choose>
<c:when test="${sale.shipping}">
<table>
<tr class="readOnly">
<td>Type:</td>
<td>${sale.shippingType}</td>
</tr>
<tr class="readOnly">
<td>Date:</td>
<td>
<spring:bind path="sale.shipDate">
${status.value}
</spring:bind>
</td>
</tr>
</table>
</c:when>
<c:otherwise>
No shipping required: you're picking up the items
</c:otherwise>
</c:choose>
</td>
</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.jsp"/>">
<input type="submit" class="button" value="Home">
</form>
</td>
</tr>
</table>
</div>
<%@ 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" %>

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,22 @@
<%@ page contentType="text/html" %>
<%@ page session="false" %>
<%@ 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" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<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">
</div>
<div id="navigation">
</div>

View File

@@ -0,0 +1,27 @@
<%@ include file="includeTop.jsp" %>
<div id="content">
<div id="insert"><img src="images/webflow-logo.jpg"/></div>
<h2>Enter price and item count</h2>
<hr>
<table>
<form:form commandName="sale" method="post">
<tr>
<td>Price:</td>
<td><form:input path="price" /></td><td><form:errors path="price" /></td>
</tr>
<tr>
<td>Item count:</td>
<td><form:input path="itemCount" /></td><td><form:errors path="itemCount" /></td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
<input type="submit" class="button" name="_eventId_submit" value="Next">
</td>
</tr>
</form:form>
</table>
</div>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,51 @@
<%@ include file="includeTop.jsp" %>
<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>${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><td>${sale.shipping}</td>
</tr>
<form:form commandName="sale" method="post">
<tr>
<td>Shipping type:</td>
<td>
<spring:bind path="sale.shippingType">
<select name="${status.expression}">
<option value="S" <c:if test="${status.value=='S'}">selected</c:if>>
Standard (10 extra cost)
</option>
<option value="E" <c:if test="${status.value=='E'}">selected</c:if>>
Express (20 extra cost)
</option>
</select>
</spring:bind>
</td>
</tr>
<tr>
<td>Ship date (DD/MM/YYYY):</td>
<td>
<form:input path="shipDate" />
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
<input type="submit" class="button" name="_eventId_submit" value="Next">
</td>
</tr>
</form:form>
</table>
</div>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,22 @@
<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">
<!--
A general purpose controller for the entire "Point of Sale (POS)" application,
exposed at the /pos.htm URL. The id of a flow to launch should be passed
in using the "_flowId" request parameter: e.g. /pos.htm?_flowId=sellitem-flow
-->
<bean name="/pos.htm" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor" />
</bean>
<!-- Maps flow view-state view names to JSP templates -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>

View File

@@ -0,0 +1,28 @@
<?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">
<!-- Launches new flow executions and resumes existing executions -->
<flow:executor id="flowExecutor" registry-ref="flowRegistry">
<flow:execution-listeners>
<flow:listener ref="listener" criteria="sellitem-flow" />
</flow:execution-listeners>
</flow:executor>
<!-- Creates the registry of flow definitions for this application -->
<flow:registry id="flowRegistry">
<flow:location path="/WEB-INF/flows/**/*-flow.xml" />
</flow:registry>
<!-- Observes the lifecycle of sellitem-flow executions -->
<bean id="listener"
class="org.springframework.webflow.samples.sellitem.SellItemFlowExecutionListener" />
</beans>

View File

@@ -0,0 +1,54 @@
<?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.root</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:org/springframework/webflow/samples/sellitem/services-config.xml
</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</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>
<servlet>
<servlet-name>sellitem</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/sellitem-servlet-config.xml
/WEB-INF/sellitem-webflow-config.xml
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>sellitem</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

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,58 @@
<%@ page session="true" %> <%-- make sure we have a session --%>
<HTML>
<BODY>
<DIV align="left">Sell Item - A Spring Web Flow Sample</DIV>
<HR>
<DIV align="left">
<P>
<A href="pos.htm?_flowId=sellitem-flow">Sell Item</A>
</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>
Using the "_flowId" request parameter 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>
Use of the FormAction to perform form processing, including the
FormAction's "setupForm" method to install custom property editors for
formatting text field values (shipDate).
</LI>
<LI>
Using continuations to make the flow completely stable, no matter
how browser navigation buttons are used.
</LI>
<LI>
Using "conversation invalidation after completion" to prevent duplicate submits
of the same sale while taking advantage of continuations to allow back button
usage while the application transaction is in process.
</LI>
<LI>
"Always redirect on pause" to benefit from the POST+REDIRECT+GET pattern with no special coding.
</LI>
<LI>
Using <A href="http://www.ognl.org/">OGNL</A> based conditional expressions.
</LI>
<LI>
Use of subflows to compose a multi-step business process from independently reusable modules.
</LI>
</UL>
</P>
</DIV>
<HR>
<DIV align="right"></DIV>
</BODY>
</HTML>

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);
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.easymock.EasyMock;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.execution.support.ApplicationView;
import org.springframework.webflow.test.MockFlowServiceLocator;
import org.springframework.webflow.test.MockParameterMap;
import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests;
public class SellItemFlowExecutionTests extends AbstractXmlFlowExecutionTests {
private String flowDir = "src/main/webapp/WEB-INF/flows";
private SaleProcessor saleProcessor;
@Override
protected FlowDefinitionResource getFlowDefinitionResource() {
return createFlowDefinitionResource(flowDir, "sellitem-flow.xml");
}
public void testStartFlow() {
ApplicationView selectedView = applicationView(startFlow());
assertModelAttributeNotNull("sale", selectedView);
assertViewNameEquals("priceAndItemCountForm", selectedView);
}
public void testSubmitPriceAndItemCount() {
testStartFlow();
MockParameterMap parameters = new MockParameterMap();
parameters.put("itemCount", "4");
parameters.put("price", "25");
ApplicationView selectedView = applicationView(signalEvent("submit", parameters));
assertViewNameEquals("categoryForm", selectedView);
}
public void testSubmitCategoryForm() {
testSubmitPriceAndItemCount();
MockParameterMap parameters = new MockParameterMap();
parameters.put("category", "A");
ApplicationView selectedView = applicationView(signalEvent("submit", parameters));
assertViewNameEquals("costOverview", selectedView);
assertFlowExecutionEnded();
}
public void testSubmitCategoryFormWithShipping() {
testSubmitPriceAndItemCount();
MockParameterMap parameters = new MockParameterMap();
parameters.put("category", "A");
parameters.put("shipping", "true");
ApplicationView selectedView = applicationView(signalEvent("submit", parameters));
assertViewNameEquals("shippingDetailsForm", selectedView);
}
public void testSubmitShippingDetailsForm() {
testSubmitCategoryFormWithShipping();
saleProcessor.process((Sale)getRequiredFlowAttribute("sale", Sale.class));
EasyMock.replay(saleProcessor);
MockParameterMap parameters = new MockParameterMap();
parameters.put("shippingType", "E");
parameters.put("shipDate", "12/06/2006");
ApplicationView selectedView = applicationView(signalEvent("submit", parameters));
assertViewNameEquals("costOverview", selectedView);
assertFlowExecutionEnded();
EasyMock.verify(saleProcessor);
}
@Override
protected void registerMockServices(MockFlowServiceLocator serviceRegistry) {
saleProcessor = EasyMock.createMock(SaleProcessor.class);
serviceRegistry.registerBean("saleProcessor", saleProcessor);
// we'll use real shipping flow
FlowDefinitionResource shipping = createFlowDefinitionResource(flowDir, "shipping-flow.xml");
serviceRegistry.registerSubflow(createFlow(shipping, serviceRegistry));
}
}