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,83 @@
/*
* 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.phonebook;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class Person implements Serializable {
private Long id;
private String firstName;
private String lastName;
private String userId;
private String phone;
private List<Person> colleagues = new ArrayList<Person>();
public Person() {
this(-1, "", "", "", "");
}
public Person(long id, String firstName, String lastName, String userId, String phone) {
this.id = new Long(id);
this.firstName = firstName;
this.lastName = lastName;
this.userId = userId;
this.phone = phone;
}
public Long getId() {
return id;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public String getUserId() {
return userId;
}
public String getPhone() {
return this.phone;
}
public List getColleagues() {
return this.colleagues;
}
public int getColleagueCount() {
return this.colleagues.size();
}
public Person getColleague(int i) {
return this.colleagues.get(i);
}
public void addColleague(Person colleague) {
this.colleagues.add(colleague);
}
}

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.phonebook;
import java.util.List;
public interface Phonebook {
public List<Person> search(SearchCriteria criteria);
public Person getPerson(Long id);
public Person getPerson(String userId);
}

View File

@@ -0,0 +1,41 @@
/*
* 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.phonebook;
import java.io.Serializable;
public class SearchCriteria implements Serializable {
private String firstName = "";
private String lastName = "";
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.phonebook;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class SearchCriteriaValidator implements Validator {
public boolean supports(Class clazz) {
return clazz.equals(SearchCriteria.class);
}
public void validate(Object obj, Errors errors) {
SearchCriteria query = (SearchCriteria)obj;
if (!StringUtils.hasText(query.getFirstName()) && !StringUtils.hasText(query.getLastName())) {
errors.reject("noCriteria", "Please provide some query criteria!");
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.phonebook.stub;
import java.util.ArrayList;
import java.util.List;
import org.springframework.webflow.samples.phonebook.Person;
import org.springframework.webflow.samples.phonebook.Phonebook;
import org.springframework.webflow.samples.phonebook.SearchCriteria;
public class StubPhonebook implements Phonebook {
private List<Person> persons = new ArrayList<Person>();
public StubPhonebook() {
// setup some dummy test data
Person kd = new Person(1, "Keith", "Donald", "kdonald", "11111");
Person ev = new Person(2, "Erwin", "Vervaet", "klr8", "22222");
Person cs = new Person(3, "Colin", "Sampaleanu", "sampa", "33333");
Person jh = new Person(4, "Juergen", "Hoeller", "jhoeller", "44444");
Person rj = new Person(5, "Rod", "Johnson", "rod", "55555");
Person tr = new Person(6, "Thomas", "Risberg", "trisberg", "66666");
Person aa = new Person(7, "Alef", "Arendsen", "alef", "77777");
Person mp = new Person(8, "Mark", "Pollack", "mark", "88888");
kd.addColleague(ev);
kd.addColleague(cs);
kd.addColleague(jh);
kd.addColleague(rj);
kd.addColleague(tr);
kd.addColleague(aa);
kd.addColleague(mp);
ev.addColleague(kd);
ev.addColleague(cs);
ev.addColleague(jh);
ev.addColleague(rj);
cs.addColleague(kd);
cs.addColleague(ev);
cs.addColleague(jh);
cs.addColleague(rj);
cs.addColleague(aa);
cs.addColleague(mp);
rj.addColleague(cs);
rj.addColleague(kd);
rj.addColleague(ev);
rj.addColleague(jh);
rj.addColleague(tr);
rj.addColleague(aa);
rj.addColleague(mp);
jh.addColleague(cs);
jh.addColleague(kd);
jh.addColleague(ev);
jh.addColleague(jh);
jh.addColleague(tr);
jh.addColleague(aa);
Person sa = new Person(9, "Shaun", "Alexander", "rolltide", "44444");
Person dj = new Person(10, "Darell", "Jackson", "gatorcountry", "55555");
sa.addColleague(dj);
dj.addColleague(sa);
persons.add(kd);
persons.add(ev);
persons.add(cs);
persons.add(jh);
persons.add(rj);
persons.add(tr);
persons.add(aa);
persons.add(mp);
persons.add(sa);
persons.add(dj);
}
public List<Person> search(SearchCriteria query) {
List<Person> res = new ArrayList<Person>();
for (Person person : persons) {
if ((person.getFirstName().indexOf(query.getFirstName()) != -1)
&& (person.getLastName().indexOf(query.getLastName()) != -1)) {
res.add(person);
}
}
return res;
}
public Person getPerson(Long id) {
for (Person person : persons) {
if (person.getId().equals(id)) {
return person;
}
}
return null;
}
public Person getPerson(String userId) {
for (Person person : persons) {
if (person.getUserId().equals(userId)) {
return person;
}
}
return null;
}
}

View File

@@ -0,0 +1,10 @@
<?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="phonebook" class="org.springframework.webflow.samples.phonebook.stub.StubPhonebook"/>
</beans>

View File

@@ -0,0 +1,67 @@
/*
* 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.phonebook.webflow;
import org.springframework.binding.mapping.DefaultAttributeMapper;
import org.springframework.binding.mapping.Mapping;
import org.springframework.webflow.engine.Transition;
import org.springframework.webflow.engine.builder.AbstractFlowBuilder;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
import org.springframework.webflow.engine.support.ConfigurableFlowAttributeMapper;
/**
* Java-based flow builder that builds the person details flow, exactly like it
* is defined in the <code>detail-flow.xml</code> XML flow definition.
* <p>
* This encapsulates the page flow of viewing a person's details and their
* collegues in a reusable, self-contained module.
*
* @author Keith Donald
*/
class PersonDetailFlowBuilder extends AbstractFlowBuilder {
public PersonDetailFlowBuilder(FlowServiceLocator flowServiceLocator) {
super(flowServiceLocator);
}
public void buildInputMapper() throws FlowBuilderException {
Mapping idMapping = mapping().source("id").target("flowScope.id").value();
getFlow().setInputMapper(new DefaultAttributeMapper().addMapping(idMapping));
}
public void buildStates() throws FlowBuilderException {
// get the person given a userid as input
addActionState("getDetails", action("phonebook", method("getPerson(${flowScope.id})"), result("person")),
transition(on(success()), to("displayDetails")));
// view the person details
addViewState("displayDetails", "details", new Transition[] { transition(on(back()), to("finish")),
transition(on(select()), to("browseColleagueDetails")) });
// view details for selected collegue
ConfigurableFlowAttributeMapper idMapper = new ConfigurableFlowAttributeMapper();
idMapper.addInputMapping(mapping().source("requestParameters.id").target("id").from(String.class)
.to(Long.class).value());
addSubflowState("browseColleagueDetails", getFlow(), idMapper, transition(on(finish()), to("getDetails")));
// end
addEndState("finish");
// end error
addEndState("error");
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.phonebook.webflow;
import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistrar;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.StaticFlowDefinitionHolder;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilder;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
/**
* Demonstrates how to register flows programatically.
*
* @author Keith Donald
*/
class PhonebookFlowRegistrar implements FlowDefinitionRegistrar {
private FlowServiceLocator serviceLocator;
public PhonebookFlowRegistrar(FlowServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
public void registerFlowDefinitions(FlowDefinitionRegistry registry) {
registry.registerFlowDefinition(assemble("detail-flow", new PersonDetailFlowBuilder(serviceLocator)));
registry.registerFlowDefinition(assemble("search-flow", new SearchPersonFlowBuilder(serviceLocator)));
}
private FlowDefinitionHolder assemble(String flowId, FlowBuilder flowBuilder) {
return new StaticFlowDefinitionHolder(new FlowAssembler(flowId, flowBuilder).assembleFlow());
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.phonebook.webflow;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.builder.AbstractFlowBuildingFlowRegistryFactoryBean;
/**
* Demonstrates how to populate a flow registry programatically.
*
* @author Keith Donald
*/
public class PhonebookFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistryFactoryBean {
protected void doPopulate(FlowDefinitionRegistry registry) {
new PhonebookFlowRegistrar(getFlowServiceLocator()).registerFlowDefinitions(registry);
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.phonebook.webflow;
import org.springframework.webflow.action.FormAction;
import org.springframework.webflow.action.MultiAction;
import org.springframework.webflow.engine.Transition;
import org.springframework.webflow.engine.builder.AbstractFlowBuilder;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
import org.springframework.webflow.engine.support.ConfigurableFlowAttributeMapper;
import org.springframework.webflow.execution.ScopeType;
import org.springframework.webflow.samples.phonebook.SearchCriteria;
import org.springframework.webflow.samples.phonebook.SearchCriteriaValidator;
/**
* Java-based flow builder that searches for people in the phonebook. The flow
* defined by this class is exactly the same as that defined in the
* <code>search-flow.xml</code> XML flow definition.
* <p>
* This encapsulates the page flow of searching for some people, selecting a
* person you care about, and viewing their person's details and those of their
* collegues in a reusable, self-contained module.
*
* @author Keith Donald
*/
class SearchPersonFlowBuilder extends AbstractFlowBuilder {
public SearchPersonFlowBuilder(FlowServiceLocator flowServiceLocator) {
super(flowServiceLocator);
}
public void buildStates() throws FlowBuilderException {
// view search criteria
MultiAction searchFormAction = createSearchFormAction();
addViewState("enterCriteria", "searchCriteria", invoke("setupForm", searchFormAction),
new Transition[] { transition(on("search"), to("executeSearch"), ifReturnedSuccess(invoke(
"bindAndValidate", searchFormAction))) });
// execute query
addActionState("executeSearch", action("phonebook", method("search(${flowScope.searchCriteria})"),
result("results")), transition(on(success()), to("displayResults")));
// view results
addViewState("displayResults", "searchResults", new Transition[] {
transition(on("newSearch"), to("enterCriteria")), transition(on(select()), to("browseDetails")) });
// view details for selected user id
ConfigurableFlowAttributeMapper idMapper = new ConfigurableFlowAttributeMapper();
idMapper.addInputMapping(mapping().source("requestParameters.id").target("id").from(String.class)
.to(Long.class).value());
addSubflowState("browseDetails", flow("detail-flow"), idMapper, transition(on(finish()), to("executeSearch")));
// end - an error occured
addEndState(error(), "error");
}
protected FormAction createSearchFormAction() {
FormAction action = new FormAction(SearchCriteria.class);
action.setFormObjectScope(ScopeType.FLOW);
action.setValidator(new SearchCriteriaValidator());
return action;
}
}

View File

@@ -0,0 +1,17 @@
<?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.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" repository-type="continuation"/>
<!-- Creates the registry of Java-based flow definitions for this application -->
<bean id="flowRegistry" class="org.springframework.webflow.samples.phonebook.webflow.PhonebookFlowRegistryFactoryBean"/>
</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 webflow debug logging
log4j.category.org.springframework.webflow=DEBUG
log4j.category.org.springframework.binding=DEBUG

View File

@@ -0,0 +1,37 @@
<?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="id"/>
</input-mapper>
<start-state idref="displayDetails" />
<view-state id="displayDetails" view="details">
<render-actions>
<bean-action bean="phonebook" method="getPerson">
<method-arguments>
<argument expression="flowScope.id" />
</method-arguments>
<method-result name="person" />
</bean-action>
</render-actions>
<transition on="back" to="finish" />
<transition on="select" to="browseColleagueDetails" />
</view-state>
<subflow-state id="browseColleagueDetails" flow="detail-flow">
<attribute-mapper>
<input-mapper>
<mapping source="requestParameters.id" target="id" from="string" to="long" />
</input-mapper>
</attribute-mapper>
<transition on="finish" to="displayDetails" />
</subflow-state>
<end-state id="finish" />
</flow>

View File

@@ -0,0 +1,14 @@
<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">
<!-- Search form action that setups the form and processes form submissions -->
<bean id="formAction" class="org.springframework.webflow.action.FormAction">
<property name="formObjectClass" value="org.springframework.webflow.samples.phonebook.SearchCriteria"/>
<property name="validator">
<bean class="org.springframework.webflow.samples.phonebook.SearchCriteriaValidator"/>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,42 @@
<?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="enterCriteria"/>
<view-state id="enterCriteria" view="searchCriteria">
<render-actions>
<action bean="formAction" method="setupForm"/>
</render-actions>
<transition on="search" to="displayResults">
<action bean="formAction" method="bindAndValidate"/>
</transition>
</view-state>
<view-state id="displayResults" view="searchResults">
<render-actions>
<bean-action bean="phonebook" method="search">
<method-arguments>
<argument expression="flowScope.searchCriteria"/>
</method-arguments>
<method-result name="results"/>
</bean-action>
</render-actions>
<transition on="newSearch" to="enterCriteria"/>
<transition on="select" to="browseDetails"/>
</view-state>
<subflow-state id="browseDetails" flow="detail-flow">
<attribute-mapper>
<input-mapper>
<mapping source="requestParameters.id" target="id" from="string" to="long"/>
</input-mapper>
</attribute-mapper>
<transition on="finish" to="displayResults"/>
</subflow-state>
<import resource="search-flow-beans.xml"/>
</flow>

View File

@@ -0,0 +1,51 @@
<%@ include file="includeTop.jsp" %>
<div id="content">
<div id="insert">
<img src="images/webflow-logo.jpg"/>
</div>
<form action="phonebook.htm" method="post">
<table>
<tr>
<td>Person Details</td>
</tr>
<tr>
<td colpan="2"><hr></td>
</tr>
<tr>
<td><b>First Name</b></td>
<td>${person.firstName}</td>
</tr>
<tr>
<td><b>Last Name</b></td>
<td>${person.lastName}</td>
</tr>
<tr>
<td><b>User Id</B></td>
<td>${person.userId}</td>
</tr>
<tr>
<td><b>Phone</b></td>
<td>${person.phone}</td>
</tr>
<tr>
<td colspan="2">
<br>
<b>Colleagues:</b>
<br>
<c:forEach var="colleague" items="${person.colleagues}">
<a href="phonebook.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=select&id=${colleague.id}">
${colleague.firstName} ${colleague.lastName}<br>
</a>
</c:forEach>
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
<input type="submit" class="button" name="_eventId_back" value="Back">
</td>
</tr>
</table>
</form>
</DIV>

View File

@@ -0,0 +1,18 @@
<%@ page session="false" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
</HEAD>
<BODY>
<DIV align="left">Error</DIV>
<HR>
<DIV align="left">
<P>
An error has occured!
</P>
</DIV>
<HR>
<DIV align="right"></DIV>
</BODY>
</HTML>

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,21 @@
<%@ 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>Search the Phonebook</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" height="73" alt="Logo" border="0">
</div>
<div id="navigation">
</div>

View File

@@ -0,0 +1,51 @@
<%@ include file="includeTop.jsp" %>
<div id="content">
<div id="insert">
<img src="images/webflow-logo.jpg"/>
</div>
<form:form commandName="searchCriteria" method="post">
<table>
<tr>
<td>Search Criteria</td>
</tr>
<tr>
<td colspan="2">
<hr>
</td>
</tr>
<spring:hasBindErrors name="searchCriteria">
<tr>
<td colspan="2">
<div class="error">Please provide valid search criteria</div>
</td>
</tr>
</spring:hasBindErrors>
<tr>
<td>First Name</td>
<td>
<form:input path="firstName" />
</td>
</tr>
<TR>
<td>Last Name</td>
<td>
<form:input path="lastName" />
</td>
</TR>
<tr>
<td colspan="2">
<hr>
</td>
</tr>
<tr>
<td colspan="2" class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
<input type="submit" class="button" name="_eventId_search" value="Search">
</td>
</tr>
</table>
</form:form>
</div>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,53 @@
<%@ include file="includeTop.jsp" %>
<div id="content">
<div id="insert">
<img src="images/webflow-logo.jpg"/>
</div>
<form action="phonebook.htm" method="post">
<table>
<tr>
<td>
Search Results
</td>
</tr>
<tr>
<td>
<hr>
</td>
</tr>
<tr>
<td>
<table border="1">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>User Id</th>
<th>Phone</th>
</tr>
<c:forEach var="person" items="${results}">
<tr>
<td>${person.firstName}</td>
<td>${person.lastName}</td>
<td>
<a href="phonebook.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=select&id=${person.id}">
${person.userId}
</a>
</td>
<td>${person.phone}</td>
</tr>
</c:forEach>
</table>
</td>
</tr>
<tr>
<td class="buttonBar">
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
<input type="submit" class="button" name="_eventId_newSearch" value="New Search">
</td>
</tr>
</table>
</form>
</div>
<%@ include file="includeBottom.jsp" %>

View File

@@ -0,0 +1,25 @@
<?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">
<!--
Exposes web flows for execution at a single request URL.
The id of a flow to launch should be passed in by clients using
the "_flowId" request parameter:
e.g. /phonebook.htm?_flowId=search
-->
<bean name="/phonebook.htm" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor"/>
<property name="cacheSeconds" value="5"/>
</bean>
<!-- Resolves flow 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,19 @@
<?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"/>
<!-- Creates the registry of flow definitions for this application -->
<flow:registry id="flowRegistry">
<flow:location path="/WEB-INF/flows/**-flow.xml"/>
</flow:registry>
</beans>

View File

@@ -0,0 +1,43 @@
<?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>contextConfigLocation</param-name>
<param-value>
classpath:org/springframework/webflow/samples/phonebook/stub/services-config.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>phonebook</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/phonebook-servlet-config.xml
/WEB-INF/phonebook-webflow-config.xml
<!-- Comment out to use alternative Java-based Flow Builders
classpath:org/springframework/webflow/samples/phonebook/webflow/phonebook-webflow-config.xml
-->
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>phonebook</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,21 @@
<%@ page session="true" %> <%-- make sure we have a session --%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
</HEAD>
<BODY>
<DIV align="left">Phonebook - A Spring Web Flow Sample</DIV>
<HR>
<DIV align="left">
<P>
<A href="phonebook.htm?_flowId=search-flow">Phonebook</A>
</P>
<P>
This sample application illustrates core features of the web flow system.
</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,92 @@
/*
* 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.phonebook.webflow;
import org.springframework.binding.mapping.AttributeMapper;
import org.springframework.binding.mapping.MappingContext;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.engine.EndState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.execution.support.ApplicationView;
import org.springframework.webflow.samples.phonebook.stub.StubPhonebook;
import org.springframework.webflow.test.MockFlowServiceLocator;
import org.springframework.webflow.test.MockParameterMap;
import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests;
public class SearchFlowExecutionTests extends AbstractXmlFlowExecutionTests {
public void testStartFlow() {
ApplicationView view = applicationView(startFlow());
assertCurrentStateEquals("enterCriteria");
assertViewNameEquals("searchCriteria", view);
assertModelAttributeNotNull("searchCriteria", view);
}
public void testCriteriaSubmitSuccess() {
startFlow();
MockParameterMap parameters = new MockParameterMap();
parameters.put("firstName", "Keith");
parameters.put("lastName", "Donald");
ApplicationView view = applicationView(signalEvent("search", parameters));
assertCurrentStateEquals("displayResults");
assertViewNameEquals("searchResults", view);
assertModelAttributeCollectionSize(1, "results", view);
}
public void testCriteriaSubmitError() {
startFlow();
signalEvent("search");
assertCurrentStateEquals("enterCriteria");
}
public void testNewSearch() {
testCriteriaSubmitSuccess();
ApplicationView view = applicationView(signalEvent("newSearch"));
assertCurrentStateEquals("enterCriteria");
assertViewNameEquals("searchCriteria", view);
}
public void testSelectValidResult() {
testCriteriaSubmitSuccess();
MockParameterMap parameters = new MockParameterMap();
parameters.put("id", "1");
ApplicationView view = applicationView(signalEvent("select", parameters));
assertCurrentStateEquals("displayResults");
assertViewNameEquals("searchResults", view);
assertModelAttributeCollectionSize(1, "results", view);
}
@Override
protected FlowDefinitionResource getFlowDefinitionResource() {
return createFlowDefinitionResource("src/main/webapp/WEB-INF/flows/search-flow.xml");
}
@Override
protected void registerMockServices(MockFlowServiceLocator serviceRegistry) {
Flow mockDetailFlow = new Flow("detail-flow");
mockDetailFlow.setInputMapper(new AttributeMapper() {
public void map(Object source, Object target, MappingContext context) {
assertEquals("id of value 1 not provided as input by calling search flow", new Long(1), ((AttributeMap)source).get("id"));
}
});
// test responding to finish result
new EndState(mockDetailFlow, "finish");
serviceRegistry.registerSubflow(mockDetailFlow);
serviceRegistry.registerBean("phonebook", new StubPhonebook());
}
}