Updated to work with Web Flow 2.0. Added Spring Security integration.

This commit is contained in:
Scott Andrews
2008-03-05 20:57:57 +00:00
parent f214dd00e1
commit 266ed30fec
35 changed files with 483 additions and 855 deletions

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="lib" path="lib/test/junit.jar"/>

View File

@@ -4,6 +4,8 @@
<wb-resource deploy-path="/" source-path="/src/main/webapp"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/lib" source-path="/lib/global"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/resources"/>
<property name="java-output-path" value="target/classes"/>
<property name="context-root" value="/swf-booking-mvc"/>
</wb-module>

View File

@@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<configExtensions>
<configExtension>xml</configExtension>
</configExtensions>
<version>1</version>
<pluginVersion><![CDATA[2.0.4.v200802202100]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>src/main/webapp/WEB-INF/config/application-layer-config.xml</config>
<config>src/main/webapp/WEB-INF/config/web-application-config.xml</config>
<config>src/main/webapp/flow/main/main-beans.xml</config>
<config>src/main/webapp/flow/booking/booking-beans.xml</config>
</configs>
<configSets>
<configSet>
@@ -15,8 +14,6 @@
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
<incomplete>false</incomplete>
<configs>
<config>src/main/webapp/WEB-INF/config/application-layer-config.xml</config>
<config>src/main/webapp/WEB-INF/config/web-application-config.xml</config>
</configs>
</configSet>
<configSet>
@@ -24,8 +21,6 @@
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
<incomplete>false</incomplete>
<configs>
<config>src/main/webapp/flow/booking/booking-beans.xml</config>
<config>src/main/webapp/WEB-INF/config/application-layer-config.xml</config>
</configs>
</configSet>
<configSet>
@@ -33,8 +28,6 @@
<allowBeanDefinitionOverriding>true</allowBeanDefinitionOverriding>
<incomplete>false</incomplete>
<configs>
<config>src/main/webapp/flow/main/main-beans.xml</config>
<config>src/main/webapp/WEB-INF/config/application-layer-config.xml</config>
</configs>
</configSet>
</configSets>

View File

@@ -11,24 +11,27 @@
<dependencies defaultconf="global->default">
<!-- global dependencies -->
<dependency org="log4j" name="log4j" rev="1.2.14"/>
<dependency org="log4j" name="log4j" rev="1.2.14"/>
<dependency org="taglibs" name="standard" rev="1.1.2"/>
<dependency org="jstl" name="jstl" rev="1.1.2"/>
<dependency org="aopalliance" name="aopalliance" rev="1.0" />
<dependency org="javax.servlet" name="jstl" rev="1.1.2"/>
<dependency org="aopalliance" name="aopalliance" rev="1.0" />
<dependency org="commons-codec" name="commons-codec" rev="1.3"/>
<dependency org="org.apache.tiles" name="tiles-api" rev="2.0.4" />
<dependency org="org.apache.tiles" name="tiles-core" rev="2.0.4" />
<dependency org="org.apache.tiles" name="tiles-jsp" rev="2.0.4" />
<dependency org="org.springframework" name="spring-faces" rev="latest.integration" />
<dependency org="org.springframework" name="spring-webflow" rev="latest.integration" conf="global->mvc"/>
<dependency org="org.springframework" name="spring-aop" rev="2.5"/>
<dependency org="org.springframework" name="spring-orm" rev="2.5"/>
<dependency org="org.springframework" name="spring-jdbc" rev="2.5"/>
<dependency org="org.springframework" name="spring-tx" rev="2.5"/>
<dependency org="org.springframework" name="spring-aop" rev="2.5.2"/>
<dependency org="org.springframework" name="spring-orm" rev="2.5.2"/>
<dependency org="org.springframework" name="spring-jdbc" rev="2.5.2"/>
<dependency org="org.springframework" name="spring-tx" rev="2.5.2"/>
<dependency org="org.springframework.security" name="spring-security-core" rev="2.0-M2"/>
<dependency org="org.springframework.security" name="spring-security-taglibs" rev="2.0-M2"/>
<dependency org="jboss" name="jboss-el" rev="1.0"/>
<dependency org="org.hibernate" name="hibernate" rev="3.2.5.ga" />
<dependency org="org.hibernate" name="hibernate-entitymanager" rev="3.3.1.ga"/>
<!-- Required by "jboss archive browsing" which is used by hibernate-entity manager but not noted in their docs -->
<dependency org="concurrent" name="concurrent" rev="1.3.4" />
<!-- Required by "jboss archive browsing" which is used by hibernate-entity manager but not noted in their docs -->
<dependency org="concurrent" name="concurrent" rev="1.3.4" />
<dependency org="org.hibernate" name="hibernate-commons-annotations" rev="3.3.0.ga"/>
<dependency org="javax.persistence" name="persistence-api" rev="1.0b"/>
<dependency org="hsqldb" name="hsqldb" rev="1.8.0.7"/>
@@ -39,7 +42,7 @@
<!-- test-time only dependencies -->
<dependency org="junit" name="junit" rev="3.8.2" conf="test->default" />
<dependency org="org.springframework" name="spring-test" rev="2.5" conf="test->default"/>
<dependency org="org.springframework" name="spring-test" rev="2.5.2" conf="test->default"/>
</dependencies>
</ivy-module>

View File

@@ -1,4 +1,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.io.Serializable;
import java.math.BigDecimal;
@@ -44,17 +44,18 @@ public class Booking implements Serializable {
private int beds;
public Booking() {
}
public Booking(Hotel hotel, User user) {
this.hotel = hotel;
this.user = user;
Calendar calendar = Calendar.getInstance();
setCheckinDate(calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH, 1);
setCheckoutDate(calendar.getTime());
}
public Booking(Hotel hotel, User user) {
this();
this.hotel = hotel;
this.user = user;
}
@Transient
public BigDecimal getTotal() {
return hotel.getPrice().multiply(new BigDecimal(getNights()));

View File

@@ -1,4 +1,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.util.List;
@@ -17,12 +17,10 @@ public interface BookingService {
/**
* Find hotels available for booking by some criteria.
* @param searchString the search query string to filter hotels by name
* @param pageSize the page size
* @param page the current page
* @param searchCriteria the search criteria
* @return a list of hotels not exceeding the page size
*/
public List<Hotel> findHotels(String searchString, int pageSize, int page);
public List<Hotel> findHotels(SearchCriteria searchCriteria);
/**
* Find hotels by their identifier.
@@ -36,4 +34,11 @@ public interface BookingService {
* @param id the booking id
*/
public void cancelBooking(Long id);
/**
* Lookup a user based on their username
* @param username the user's username
* @return the user
*/
public User findUser(String username);
}

View File

@@ -0,0 +1,31 @@
package org.springframework.webflow.samples.booking;
import java.util.Calendar;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class BookingValidator implements Validator {
public boolean supports(Class clazz) {
return Booking.class.equals(clazz);
}
public void validate(Object obj, Errors errors) {
Booking booking = (Booking) obj;
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1);
if (booking.getCreditCard() == null || "".equals(booking.getCreditCard())) {
errors.rejectValue("creditCard", null, "Credit card number must be provided");
}
if (booking.getCreditCardName() == null || "".equals(booking.getCreditCardName())) {
errors.rejectValue("creditCardName", null, "Credit card name must be provided");
}
if (booking.getCheckinDate() == null || booking.getCheckinDate().before(calendar.getTime())) {
errors.rejectValue("checkinDate", null, "Check in date must be a future date");
} else if (booking.getCheckoutDate() == null || !booking.getCheckinDate().before(booking.getCheckoutDate())) {
errors.rejectValue("checkoutDate", null, "Check out date must be later than check in date");
}
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.io.Serializable;
import java.math.BigDecimal;
@@ -96,6 +96,10 @@ public class Hotel implements Serializable {
this.price = price;
}
public Booking createBooking(User user) {
return new Booking(this, user);
}
@Override
public String toString() {
return "Hotel(" + name + "," + address + "," + city + "," + zip + ")";

View File

@@ -1,4 +1,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.util.List;
@@ -27,19 +27,24 @@ public class JpaBookingService implements BookingService {
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<Booking> findBookings(String username) {
return em.createQuery("select b from Booking b where b.user.username = :username order by b.checkinDate")
.setParameter("username", username).getResultList();
if (username != null) {
return em.createQuery("select b from Booking b where b.user.username = :username order by b.checkinDate")
.setParameter("username", username).getResultList();
} else {
return null;
}
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<Hotel> findHotels(String searchString, int pageSize, int page) {
String pattern = !StringUtils.hasText(searchString) ? "'%'" : "'%"
+ searchString.toLowerCase().replace('*', '%') + "%'";
public List<Hotel> findHotels(SearchCriteria search) {
String pattern = !StringUtils.hasText(search.getSearchString()) ? "%" : "%"
+ search.getSearchString().toLowerCase().replace('*', '%') + "%";
return em.createQuery(
"select h from Hotel h where lower(h.name) like " + pattern + " or lower(h.city) like " + pattern
+ " or lower(h.zip) like " + pattern + " or lower(h.address) like " + pattern).setMaxResults(
pageSize).setFirstResult(page * pageSize).getResultList();
"select h from Hotel h where lower(h.name) like :pattern or lower(h.city) like :pattern "
+ "or lower(h.zip) like :pattern or lower(h.address) like :pattern").setParameter("pattern",
pattern).setMaxResults(search.getPageSize()).setFirstResult(search.getPage() * search.getPageSize())
.getResultList();
}
@Transactional(readOnly = true)
@@ -55,4 +60,10 @@ public class JpaBookingService implements BookingService {
em.remove(booking);
}
}
@Transactional(readOnly = true)
public User findUser(String username) {
return (User) em.createQuery("select u from User u where u.username = :username").setParameter("username",
username).getSingleResult();
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.webflow.samples.booking;
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 PropertyEditors implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
}

View File

@@ -1,8 +1,7 @@
package org.springframework.webflow.samples.booking.flow.main;
package org.springframework.webflow.samples.booking;
import java.io.Serializable;
import org.springframework.webflow.samples.booking.app.BookingService;
/**
* A backing bean for the main hotel search form. Encapsulates the criteria needed to perform a hotel search.

View File

@@ -1,4 +1,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.io.Serializable;

View File

@@ -1,72 +0,0 @@
package org.springframework.webflow.samples.booking.flow.booking;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.persistence.EntityManager;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.webflow.action.FormAction;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.samples.booking.app.Booking;
import org.springframework.webflow.samples.booking.app.Hotel;
import org.springframework.webflow.samples.booking.app.User;
/**
* Actions invoked by the booking flow. These actions are extensions of the flow definition, called by the flow
* definition at the appropriate points. Actions allow an externalized flow definition to delegate out to Java code to
* perform processing.
*/
public class BookingActions extends FormAction {
public BookingActions() {
setFormObjectName("booking");
setValidator(new BookingValidator());
}
@Override
protected void initBinder(RequestContext context, DataBinder binder) {
binder.setRequiredFields(new String[] { "checkinDate", "checkoutDate", "creditCard", "creditCardName" });
}
@Override
protected void registerPropertyEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
@Override
/**
* Create a new booking object and register it with the flow-managed entity manager. The booking is not actually
* flushed to the database at this time; that only occurs when the booking flow reaches its "bookingAuthorized"
* end-state, which is a "commit" state.
*
* It is expected a future milestone of Spring Web Flow 2.0 will support Flows being defined fully in Java and
* Groovy, allowing logic like this to be defined with the flow definition and without the attribute lookup code you
* see here.
* @param context the current flow execution request context
* @return success if the booking was created successfully.
*/
protected Object createFormObject(RequestContext context) throws Exception {
Hotel hotel = (Hotel) context.getFlowScope().get("hotel");
User user = (User) context.getConversationScope().get("user");
Booking booking = new Booking(hotel, user);
EntityManager em = (EntityManager) context.getFlowScope().get("entityManager");
em.persist(booking);
return booking;
}
public class BookingValidator implements Validator {
public boolean supports(Class clazz) {
return Booking.class.equals(clazz);
}
public void validate(Object object, Errors errors) {
}
}
}

View File

@@ -1,69 +0,0 @@
package org.springframework.webflow.samples.booking.flow.main;
import java.util.List;
import org.springframework.validation.DataBinder;
import org.springframework.webflow.action.FormAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.samples.booking.app.Booking;
import org.springframework.webflow.samples.booking.app.BookingService;
import org.springframework.webflow.samples.booking.app.Hotel;
import org.springframework.webflow.samples.booking.app.User;
/**
* Actions invoked by the main flow. These actions are extensions of the flow definition, called by the flow definition
* at the appropriate points. Actions allow an externalized flow definition to delegate out to Java code to perform
* processing.
*/
public class MainActions extends FormAction {
private BookingService bookingService;
/**
* Constructs a new multi-action for the main flow that will delegate to the provided booking service
* @param bookingService the booking service
*/
public MainActions(BookingService bookingService) {
this.bookingService = bookingService;
}
/**
* Simply put a dummy user in conversation scope to simulate a user login. In the future this sample may add user
* authentication support.
* @param context the current flow execution request context
* @return success
*/
public Event initCurrentUser(RequestContext context) {
User user = new User("springer", "springrocks", "Springer");
context.getConversationScope().put("user", user);
return success();
}
/**
* Find all active bookings made by the current user.
* @param context the current flow execution request context
* @return success
*/
public Event findCurrentUserBookings(RequestContext context) {
User user = (User) context.getConversationScope().get("user");
List<Booking> bookings = bookingService.findBookings(user.getUsername());
context.getFlowScope().put("bookings", bookings);
return success();
}
/**
* Find all hotels that meet the current search criteria in flow scope.
* @param context the current flow execution request context
* @return success
*/
public Event findHotels(RequestContext context) throws Exception {
SearchCriteria search = (SearchCriteria) context.getFlowScope().get("searchCriteria");
DataBinder binder = createBinder(context, search);
doBind(context, binder);
List<Hotel> hotels = bookingService
.findHotels(search.getSearchString(), search.getPageSize(), search.getPage());
context.getFlowScope().put("hotels", hotels);
return success();
}
}

View File

@@ -5,9 +5,9 @@
version="1.0">
<persistence-unit name="bookingDatabase">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.springframework.webflow.samples.booking.app.User</class>
<class>org.springframework.webflow.samples.booking.app.Booking</class>
<class>org.springframework.webflow.samples.booking.app.Hotel</class>
<class>org.springframework.webflow.samples.booking.User</class>
<class>org.springframework.webflow.samples.booking.Booking</class>
<class>org.springframework.webflow.samples.booking.Hotel</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />

View File

@@ -1,4 +1,6 @@
insert into Customer (username, password, name) values ('springer', 'springrocks', 'Springer')
insert into Customer (username, name) values ('rod', 'Rod')
insert into Customer (username, name) values ('dianne', 'Dianne')
insert into Customer (username, name) values ('scott', 'Scott')
insert into Hotel (id, price, name, address, city, state, zip, country) values (1, 199, 'Westin Diplomat', '3555 S. Ocean Drive', 'Hollywood', 'FL', '33019', 'USA')
insert into Hotel (id, price, name, address, city, state, zip, country) values (2, 120, 'Marriott Courtyard', 'Tower Place, Buckhead', 'Atlanta', 'GA', '30305', 'USA')
insert into Hotel (id, price, name, address, city, state, zip, country) values (3, 180, 'Doubletree', 'Tower Place, Buckhead', 'Atlanta', 'GA', '30305', 'USA')

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<logger name="org.springframework.beans">
<level value="warn" />
</logger>
<logger name="org.springframework.jdbc">
<level value="warn" />
</logger>
<logger name="org.springframework.transaction">
<level value="warn" />
</logger>
<logger name="org.springframework.orm">
<level value="warn" />
</logger>
<logger name="org.springframework.web">
<level value="debug" />
</logger>
<logger name="org.springframework.webflow">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
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.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- The central service of this application that can query hotels and bookings, as well as cancel bookings -->
<bean id="bookingService" class="org.springframework.webflow.samples.booking.JpaBookingService" />
<!-- Creates a EntityManagerFactory for use with the Hibernate JPA provider and a simple in-memory data source populated with test data -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
</bean>
<!-- Deploys a in-memory "booking" datasource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:booking" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<!-- Executes transactions around @Transactional methods -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Drives transactions using local JPA APIs -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- Automatically injects EntityManager references into data access objects that require one -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- Maps JPA exceptions that occur to Spring's DataAccessException hierarchy -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
</beans>

View File

@@ -1,60 +0,0 @@
<?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:web="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<!-- Imports the "application-layer" definining business logic and data access services -->
<import resource="application-layer-config.xml"/>
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/intro=urlFilenameController
</value>
</property>
<property name="defaultHandler" ref="flowController" />
</bean>
<bean id="flowController" class="org.springframework.webflow.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor" />
</bean>
<web:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<web:flow-execution-listeners>
<web:listener ref="jpaFlowExecutionListener" criteria="*"/>
</web:flow-execution-listeners>
</web:flow-executor>
<web:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<web:flow-location path="flow/main/main.xml" />
<web:flow-location path="flow/booking/booking.xml" />
<web:flow-builder class="org.springframework.faces.ui.resource.ResourcesFlowBuilder" />
</web:flow-registry>
<bean id="flowBuilderServices" class="org.springframework.webflow.engine.builder.support.FlowBuilderServices">
<property name="viewFactoryCreator">
<bean class="org.springframework.webflow.mvc.MvcViewFactoryCreator" />
</property>
</bean>
<!-- Installs a listener that manages JPA persistence contexts for flows that require them -->
<bean id="jpaFlowExecutionListener" class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
<constructor-arg ref="entityManagerFactory" />
<constructor-arg ref="transactionManager" />
</bean>
<!-- Handles requests to render view templates directly: maps the requested path to a logical view name resolved by the resolver below -->
<bean id="urlFilenameController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
<!-- Maps logical view names selected by the url filename controller to .jsp view templates within the root webapp directory -->
<bean id="internalJspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>

View File

@@ -0,0 +1,28 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tiles:insertTemplate template="/WEB-INF/layouts/standard.jsp">
<tiles:putAttribute name="content">
<div class="section">
<h1>Welcome to the Spring Web Flow + Spring MVC Sample Application</h1>
<p>
This hotel booking sample application illustrates Spring Web Flow in a Spring MVC environment.
The key features illustrated in this sample include:
</p>
<ul>
<li>A unified navigation model</li>
<li>A robust state management model</li>
<li>Modularization of web application functionality by domain responsibility</li>
<li>Flow-managed persistence contexts with the Java Persistence API (JPA)</li>
<li>OGNL Expression Language (EL) integration</li>
<li>Spring IDE integration, with support for graphical flow modeling</li>
</ul>
<p align="right">
<a href="main">Start your hotel booking experience</a>
</p>
</div>
</tiles:putAttribute>
</tiles:insertTemplate>

View File

@@ -1,17 +1,18 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Spring Faces: Hotel Booking Sample Application</title>
<style type="text/css" media="screen">
@import url("/swf-booking-mvc/spring/resources/css-framework/css/tools.css");
@import url("/swf-booking-mvc/spring/resources/css-framework/css/typo.css");
@import url("/swf-booking-mvc/spring/resources/css-framework/css/forms.css");
@import url("/swf-booking-mvc/spring/resources/css-framework/css/layout-navtop-localleft.css");
@import url("/swf-booking-mvc/spring/resources/css-framework/css/layout.css");
@import url("/swf-booking-mvc/spring/resources/css/booking.css");
@import url("<c:url value="/resources/css-framework/css/tools.css" />");
@import url("<c:url value="/resources/css-framework/css/typo.css" />");
@import url("<c:url value="/resources/css-framework/css/forms.css" />");
@import url("<c:url value="/resources/css-framework/css/layout-navtop-localleft.css" />");
@import url("<c:url value="/resources/css-framework/css/layout.css" />");
@import url("<c:url value="/resources/css/booking.css" />");
</style>
</head>
<body class="tundra spring">
@@ -20,7 +21,15 @@
<div id="welcome">
<div class="left">Spring Web Flow + Spring MVC: Hotel Booking Sample Application</div>
<div class="right">
Welcome, ${user.name}
<security:authorize ifAllGranted="ROLE_USER">
<c:if test="${not empty currentUser}">
Welcome ${currentUser.name}!
</c:if>
<a href="<c:url value="/spring/logout" />">Logout</a>
</security:authorize>
<security:authorize ifAllGranted="ROLE_ANONYMOUS">
<a href="<c:url value="/spring/login" />">Login</a>
</security:authorize>
</div>
</div>
<div id="branding" class="spring">

View File

@@ -0,0 +1,62 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page import="org.springframework.security.ui.AbstractProcessingFilter" %>
<%@ page import="org.springframework.security.ui.webapp.AuthenticationProcessingFilter" %>
<%@ page import="org.springframework.security.AuthenticationException" %>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tiles:insertTemplate template="/WEB-INF/layouts/standard.jsp">
<tiles:putAttribute name="content">
<h1>Login</h1>
<c:if test="${not empty param.login_error}">
<div class="errors">
Your login attempt was not successful, try again.<br /><br />
Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
</div>
</c:if>
<div class="section">
<p>Valid users:</p>
<ul>
<li>rod/koala</li>
<li>dianne/emu</li>
<li>scott/wombat</li>
</ul>
</div>
<div class="section">
<form name="f" action="<c:url value="/spring/login-process" />" method="post">
<fieldset>
<div class="field">
<div class="label">User:</div>
<div class="output">
<input type="text" name="j_username" <c:if test="${not empty param.login_error}">value="<%= session.getAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY) %>"</c:if>>
</div>
</div>
<div class="field">
<div class="label">Password:</div>
<div class="output">
<input type="password" name="j_password" />
</div>
</div>
<div class="field">
<div class="label">Don't ask for my password for two weeks:</div>
<div class="output">
<input type="checkbox" name="_spring_security_remember_me">
</div>
</div>
</fieldset>
<div class="buttonGroup">
<input name="submit" type="submit" value="Login" />
</div>
</form>
</div>
</tiles:putAttribute>
</tiles:insertTemplate>

View File

@@ -0,0 +1,16 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tiles:insertTemplate template="/WEB-INF/layouts/standard.jsp">
<tiles:putAttribute name="content">
<div class="section">
<h1>Logout</h1>
<p>You have successfully logged out.</p>
</div>
</tiles:putAttribute>
</tiles:insertTemplate>

View File

@@ -0,0 +1,148 @@
<?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:web="http://www.springframework.org/schema/webflow-config"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.xsd">
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/intro=urlFilenameController
/login=urlFilenameController
/logout-success=urlFilenameController
</value>
</property>
<property name="defaultHandler" ref="flowController" />
</bean>
<bean id="flowController" class="org.springframework.webflow.mvc.FlowController">
<constructor-arg ref="flowExecutor" />
</bean>
<web:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<web:flow-execution-listeners>
<web:listener ref="jpaFlowExecutionListener" criteria="*" />
<web:listener ref="securityFlowExecutionListener" criteria="*" />
</web:flow-execution-listeners>
</web:flow-executor>
<web:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<web:flow-location path="WEB-INF/flows/main/main.xml" />
<web:flow-location path="WEB-INF/flows/booking/booking.xml" />
</web:flow-registry>
<web:flow-builder-services id="flowBuilderServices" expression-parser="expressionParser" />
<bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
<constructor-arg>
<bean class="org.jboss.el.ExpressionFactoryImpl" />
</constructor-arg>
</bean>
<!-- Installs a listener that manages JPA persistence contexts for flows that require them -->
<bean id="jpaFlowExecutionListener" class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
<constructor-arg ref="entityManagerFactory" />
<constructor-arg ref="transactionManager" />
</bean>
<!-- Installs a listener to apply Spring Security authorities -->
<bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener" />
<!-- Handles requests to render view templates directly: maps the requested path to a logical view name resolved by the resolver below -->
<bean id="urlFilenameController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
<!-- Maps logical view names selected by the url filename controller to .jsp view templates within the root webapp directory -->
<bean id="internalJspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- Handels form binding for the hotel search -->
<bean id="mainActions" class="org.springframework.webflow.action.FormAction">
<property name="formObjectClass" value="org.springframework.webflow.samples.booking.SearchCriteria" />
</bean>
<!-- Handels form binding and validation for the hotel booking -->
<bean id="bookingActions" class="org.springframework.webflow.action.FormAction">
<property name="formObjectClass" value="org.springframework.webflow.samples.booking.Booking" />
<property name="propertyEditorRegistrar">
<bean class="org.springframework.webflow.samples.booking.PropertyEditors" />
</property>
<property name="validator">
<bean class="org.springframework.webflow.samples.booking.BookingValidator" />
</property>
</bean>
<!-- The central service of this application that can query hotels and bookings, as well as cancel bookings -->
<bean id="bookingService" class="org.springframework.webflow.samples.booking.JpaBookingService" />
<!-- Creates a EntityManagerFactory for use with the Hibernate JPA provider and a simple in-memory data source populated with test data -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<!-- Deploys a in-memory "booking" datasource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:booking" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<!-- Executes transactions around @Transactional methods -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Drives transactions using local JPA APIs -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- Automatically injects EntityManager references into data access objects that require one -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- Maps JPA exceptions that occur to Spring's DataAccessException hierarchy -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- Configure Spring Security -->
<security:http auto-config="true">
<!-- restrict URLs based on role -->
<security:intercept-url pattern="/spring/login*" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/spring/logout-success*" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/spring/logout*" access="ROLE_USER" />
<!-- override default login and logout pages -->
<security:form-login login-page="/spring/login" login-url="/spring/login-process" default-target-url="/spring/main" authentication-failure-url="/spring/login?login_error=1" />
<security:logout logout-url="/spring/logout" logout-success-url="/spring/logout-success" />
</security:http>
<!--
Define local authentication provider, a real app would use an external provider (JDBC, LDAP, CAS, etc)
usernames/passwords are:
rod/koala
dianne/emu
scott/wombat
-->
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:user-service>
<security:user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_USER, ROLE_SUPERVISOR" />
<security:user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER" />
<security:user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</beans>

View File

@@ -4,9 +4,52 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<!--
- Location of the XML file that defines the root application context
- Applied by ContextLoaderListener.
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/web-application-config.xml
</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
- Loads the root application context of this web app at startup.
- The application context is then available via
- WebApplicationContextUtils.getWebApplicationContext(servletContext).
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.apache.tiles.web.startup.TilesListener</listener-class>
</listener>
<!-- Serves static resource content from .jar files such as spring-faces.jar -->
<servlet>
<servlet-name>Resources Servlet</servlet-name>
<servlet-class>org.springframework.faces.ui.resource.ResourceServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<!-- Map all /resources requests to the Resource Servlet for handling -->
<servlet-mapping>
<servlet-name>Resources Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
<!-- The front controller of the Spring MVC Web application, responsible for handling all application requests -->
<servlet>
@@ -14,7 +57,7 @@
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/web-application-config.xml</param-value>
<param-value>/WEB-INF/web-application-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

View File

@@ -165,4 +165,9 @@
#footer.spring img {
float: right;
padding-right: 20px;
}
div#header a {
color: inherit;
background-color: inherit;
}

View File

@@ -1,10 +0,0 @@
<?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="bookingActions" class="org.springframework.webflow.samples.booking.flow.booking.BookingActions"/>
</beans>

View File

@@ -1,62 +0,0 @@
<?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-2.0.xsd">
<attribute name="description" value="The flow that handles the process of booking a hotel for a user"/>
<!--
Indicates this flow requires a persistence context
One will be created automatically when this flow starts; all data access will use it automatically
-->
<attribute name="persistenceContext" value="true" />
<input-mapper>
<input-attribute name="${id}" scope="flow" />
</input-mapper>
<start-state idref="displayHotel"/>
<view-state id="displayHotel" view="hotelDetails.jsp">
<render-actions>
<bean-action method="findHotelById" bean="bookingService">
<method-arguments>
<argument expression="${id}" />
</method-arguments>
<method-result name="hotel" scope="flow" />
</bean-action>
</render-actions>
<transition on="book" to="enterBookingDetails" />
<transition on="cancel" to="cancel" />
</view-state>
<view-state id="enterBookingDetails" view="bookingForm.jsp">
<render-actions>
<action method="setupForm" bean="bookingActions"/>
</render-actions>
<transition on="proceed" to="confirmBooking">
<action method="bindAndValidate" bean="bookingActions" />
</transition>
<transition on="cancel" to="cancel" />
</view-state>
<view-state id="confirmBooking" view="confirmBooking.jsp">
<transition on="confirm" to="bookingAuthorized" />
<transition on="revise" to="enterBookingDetails" />
<transition on="cancel" to="cancel" />
</view-state>
<end-state id="cancel">
<!-- Indicates any changes to managed persistent entities should NOT be committed to the database -->
<attribute name="commit" value="false" type="boolean" />
</end-state>
<end-state id="bookingAuthorized">
<!-- Indicates changes to managed persistent entities should be committed to the database at this point -->
<attribute name="commit" value="true" type="boolean" />
</end-state>
<import resource="booking-beans.xml"/>
</flow>

View File

@@ -1,135 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tiles:insertTemplate template="/template.jsp">
<tiles:putAttribute name="content">
<div class="section">
<h1>Book Hotel</h1>
</div>
<div class="section">
<form:form id="booking" modelAttribute="booking">
<form:errors path="*" cssClass="errors" />
<fieldset>
<div class="field">
<div class="label">Name:</div>
<div class="output">${hotel.name}</div>
</div>
<div class="field">
<div class="label">Address:</div>
<div class="output">${hotel.address}</div>
</div>
<div class="field">
<div class="label">City, State:</div>
<div class="output">${hotel.city}, ${hotel.state}</div>
</div>
<div class="field">
<div class="label">Zip:</div>
<div class="output">${hotel.zip}</div>
</div>
<div class="field">
<div class="label">Country:</div>
<div class="output">${hotel.country}</div>
</div>
<div class="field">
<div class="label">Nightly rate:</div>
<div class="output">
<spring:bind path="hotel.price">${status.value}</spring:bind>
</div>
</div>
<div class="field">
<div class="label">
<label for="checkinDate">Check In Date:</label>
</div>
<div class="input">
<form:errors path="checkinDate" cssClass="errors"/>
<form:input id="checkinDate" path="checkinDate"/>
</div>
</div>
<div class="field">
<div class="label">
<label for="checkoutDate">Check Out Date:</label>
</div>
<div class="input">
<form:errors path="checkoutDate" cssClass="errors"/>
<form:input id="checkoutDate" path="checkoutDate"/>
</div>
</div>
<div class="field">
<div class="label">
<label for="beds">Room Preference:</label>
</div>
<div class="input">
<form:select id="beds" path="beds">
<form:option label="One king-size bed" value="1"/>
<form:option label="Two double beds" value="2"/>
<form:option label="Three beds" value="3"/>
</form:select>
</div>
</div>
<div class="field">
<div class="label">
<label for="smoking">Smoking Preference:</label>
</div>
<div id="radio" class="input">
<form:radiobutton id="smoking" path="smoking" label="Smoking" value="true"/>
<form:radiobutton path="smoking" label="Non Smoking" value="false"/>
</div>
</div>
<div class="field">
<div class="label">
<label for="creditCard">Credit Card #:</label>
</div>
<div class="input">
<form:errors path="creditCard" cssClass="errors"/>
<form:input id="creditCard" path="creditCard"/>
</div>
</div>
<div class="field">
<div class="label">
<label for="creditCardName">Credit Card Name:</label>
</div>
<div class="input">
<form:errors path="creditCardName" cssClass="errors"/>
<form:input id="creditCardName" path="creditCardName"/>
</div>
</div>
<div class="field">
<div class="label">
<label for="creditCardExpiryMonth">Credit Card Expirty:</label>
</div>
<div class="input">
<form:select id="creditCardExpiryMonth" path="creditCardExpiryMonth">
<form:option label="Jan" value="1"/>
<form:option label="Feb" value="2"/>
<form:option label="Mar" value="3"/>
<form:option label="Apr" value="4"/>
<form:option label="May" value="5"/>
<form:option label="Jun" value="6"/>
<form:option label="Jul" value="7"/>
<form:option label="Aug" value="8"/>
<form:option label="Sep" value="9"/>
<form:option label="Oct" value="10"/>
<form:option label="Nov" value="11"/>
<form:option label="Dec" value="12"/>
</form:select>
<form:select path="creditCardExpiryYear">
<form:option label="2007" value="1"/>
<form:option label="2008" value="2"/>
<form:option label="2009" value="3"/>
</form:select>
</div>
</div>
<div class="buttonGroup">
<input type="submit" name="_eventId_proceed" value="Proceed"/>&#160;
<input type="submit" name="_eventId_cancel" value="Cancel"/>&#160;
</div>
</fieldset>
</form:form>
</div>
</tiles:putAttribute>
</tiles:insertTemplate>

View File

@@ -1,64 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tiles:insertTemplate template="/template.jsp">
<tiles:putAttribute name="content">
<div class="section">
<h1>Confirm Hotel Booking</h1>
</div>
<div class="section">
<form:form id="confirm" modelAttribute="booking">
<fieldset>
<div class="field">
<div class="label">Name:</div>
<div class="output">${hotel.name}</div>
</div>
<div class="field">
<div class="label">Address:</div>
<div class="output">${hotel.address}</div>
</div>
<div class="field">
<div class="label">City, State:</div>
<div class="output">${hotel.city}, ${hotel.state}</div>
</div>
<div class="field">
<div class="label">Zip:</div>
<div class="output">${hotel.zip}</div>
</div>
<div class="field">
<div class="label">Country:</div>
<div class="output">${hotel.country}</div>
</div>
<div class="field">
<div class="label">Total payment:</div>
<div class="output">
<spring:bind path="total">${status.value}</spring:bind>
</div>
</div>
<div class="field">
<div class="label">Check In Date:</div>
<div class="output"><spring:bind path="checkinDate">${status.value}</spring:bind></div>
</div>
<div class="field">
<div class="label">Check Out Date:</div>
<div class="output"><spring:bind path="checkoutDate">${status.value}</spring:bind></div>
</div>
<div class="field">
<div class="label">Credit Card #:</div>
<div class="output">${booking.creditCard}</div>
</div>
<div class="buttonGroup">
<input type="submit" name="_eventId_confirm" value="Confim"/>&#160;
<input type="submit" name="_eventId_revise" value="Revise"/>&#160;
<input type="submit" name="_eventId_cancel" value="Cancel"/>&#160;
</div>
</fieldset>
</form:form>
</div>
</tiles:putAttribute>
</tiles:insertTemplate>

View File

@@ -1,55 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tiles:insertTemplate template="/template.jsp">
<tiles:putAttribute name="content">
<div class="section">
<h1>View Hotel</h1>
</div>
<div class="section">
<form:form id="hotel" modelAttribute="hotel">
<fieldset>
<div class="field">
<div class="label">Name:</div>
<div class="output">${hotel.name}</div>
</div>
<div class="field">
<div class="label">Address:</div>
<div class="output">${hotel.address}</div>
</div>
<div class="field">
<div class="label">City:</div>
<div class="output">${hotel.city}</div>
</div>
<div class="field">
<div class="label">State:</div>
<div class="output">${hotel.state}</div>
</div>
<div class="field">
<div class="label">Zip:</div>
<div class="output">${hotel.zip}</div>
</div>
<div class="field">
<div class="label">Country:</div>
<div class="output">${hotel.country}</div>
</div>
<div class="field">
<div class="label">Nightly rate:</div>
<div class="output">
<spring:bind path="price">${status.value}</spring:bind>
</div>
</div>
<div class="buttonGroup">
<input type="submit" name="_eventId_book" value="Book Hotel"/>&#160;
<input type="submit" name="_eventId_cancel" value="Back to Search"/>&#160;
</div>
</fieldset>
</form:form>
</div>
</tiles:putAttribute>
</tiles:insertTemplate>

View File

@@ -1,12 +0,0 @@
<?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="mainActions" class="org.springframework.webflow.samples.booking.flow.main.MainActions">
<constructor-arg ref="bookingService" />
</bean>
</beans>

View File

@@ -1,91 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tiles:insertTemplate template="/template.jsp">
<tiles:putAttribute name="content">
<form:form modelAttribute="searchCriteria">
<div class="section">
<span class="errors">
<form:errors path="*"/>
</span>
<h1>Search Hotels</h1>
<fieldset>
<form:input path="searchString"/>
<label for="pageSize">Maximum results:</label>
<form:select path="pageSize">
<form:option label="5" value="5"/>
<form:option label="10" value="10"/>
<form:option label="20" value="20"/>
</form:select>
<input type="submit" class="button" name="_eventId_findHotels" value="Find Hotels" />
</fieldset>
</div>
<div class="section">
<table class="summary">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>City, State</th>
<th>Zip</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<c:forEach var="hotel" items="${hotels}">
<tr>
<td>${hotel.name}</td>
<td>${hotel.address}</td>
<td>${hotel.city}, ${hotel.state}, ${hotel.country}</td>
<td>${hotel.zip}</td>
<td>
<a href="${flowExecutionUrl}?_eventId=selectHotel&hotelId=${hotel.id}">View Hotel</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<div class="section">
<h1>Current Hotel Bookings</h1>
</div>
<div class="section">
<table class="summary">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>City, State</th>
<th>Check in Date</th>
<th>Check out Date</th>
<th>Confirmation Number</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<c:forEach var="booking" items="${bookings}">
<tr>
<td>${booking.hotel.name}</td>
<td>${booking.hotel.address}</td>
<td>${booking.hotel.city}, ${booking.hotel.state}</td>
<td>${booking.checkinDate}</td>
<td>${booking.checkoutDate}</td>
<td>${booking.id}</td>
<td>
<a href="${flowExecutionUrl}?_eventId=cancelBooking&bookingId=${booking.id}">Cancel</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</form:form>
</tiles:putAttribute>
</tiles:insertTemplate>

View File

@@ -1,55 +0,0 @@
<?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-2.0.xsd">
<attribute name="description" value="The main flow of the application that handles searching for hotels to book"/>
<var name="searchCriteria" class="org.springframework.webflow.samples.booking.flow.main.SearchCriteria" scope="flow" />
<start-actions>
<action method="initCurrentUser" bean="mainActions" />
<action method="findCurrentUserBookings" bean="mainActions" />
</start-actions>
<start-state idref="displayMain"/>
<view-state id="displayMain" view="main.jsp">
<transition on="findHotels" to="findHotels" />
<transition on="selectHotel" to="bookHotel" />
<transition on="cancelBooking" to="cancelBooking" />
</view-state>
<action-state id="findHotels">
<action method="findHotels" bean="mainActions" />
<transition on="success" to="displayMain" />
</action-state>
<subflow-state id="bookHotel" flow="booking">
<attribute-mapper>
<input-mapper>
<mapping source="${requestParameters.hotelId}" target="${id}" from="string" to="long" />
</input-mapper>
</attribute-mapper>
<transition on="bookingAuthorized" to="reloadCurrentUserBookings" />
<transition on="cancel" to="displayMain" />
</subflow-state>
<action-state id="cancelBooking">
<bean-action method="cancelBooking" bean="bookingService">
<method-arguments>
<argument expression="${requestParameters.bookingId}" parameter-type="long"/>
</method-arguments>
</bean-action>
<transition on="success" to="reloadCurrentUserBookings" />
</action-state>
<action-state id="reloadCurrentUserBookings">
<action method="findCurrentUserBookings" bean="mainActions" />
<transition on="success" to="displayMain" />
</action-state>
<import resource="main-beans.xml" />
</flow>

View File

@@ -1,67 +0,0 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Spring Faces: Hotel Booking Sample Application</title>
<style type="text/css" media="screen">
@import url("resources/css-framework/css/tools.css");
@import url("resources/css-framework/css/typo.css");
@import url("resources/css-framework/css/forms.css");
@import url("resources/css-framework/css/layout-navtop-localleft.css");
@import url("resources/css-framework/css/layout.css");
@import url("resources/css/booking.css");
</style>
</head>
<body class="spring">
<div id="page">
<div id="header" class="clearfix spring">
<div id="welcome">
<div class="left">Spring Faces: Hotel Booking Sample Application</div>
</div>
<div id="branding" class="spring">
<img src="<c:url value="/images/header.jpg"/>" alt="A room at the Westin Diplomat in Hollywood, Florida"/>
</div>
</div>
<div id="content" class="clearfix spring">
<div id="local" class="spring">
<a href="http://www.thespringexperience.com">
<img src="<c:url value="/images/diplomat.jpg"/>" alt="The Westin Diplomat in Hollywood, Florida"/>
</a>
<a href="http://www.thespringexperience.com">
<img src="<c:url value="/images/tse.gif"/>" alt="The Spring Experience"/>
</a>
<p>
The features illustrated in this sample are just the beginning.
To see what is in store for developing rich web applications with Spring, join us at
<a href="http://www.thespringexperience.com">The Spring Experience</a>
December 12 - 15, 2007 at the Westin Diplomat in Hollywood, Florida.
</p>
</div>
<div id="main">
<div class="section">
<h1>Welcome to the Spring Web Flow + Spring MVC Sample Application</h1>
<p>
This hotel booking sample application illustrates Spring Web Flow in a Spring MVC environment.
The key features illustrated in this sample include:
</p>
<ul>
<li>A unified navigation model</li>
<li>A robust state management model</li>
<li>Modularization of web application functionality by domain responsibility</li>
<li>Flow-managed persistence contexts with the Java Persistence API (JPA)</li>
<li>OGNL Expression Language (EL) integration</li>
<li>Spring IDE integration, with support for graphical flow modeling</li>
</ul>
<p align="right">
<a href="main">Start your hotel booking experience</a>
</p>
</div>
</div>
</div>
<div id="footer" class="clearfix spring">
<a href="http://www.springframework.org"><img src="<c:url value="/images/powered-by-spring.png"/>" alt="Powered By Spring" /></a>
</div>
</div>
</body>
</html>