simplification

This commit is contained in:
Keith Donald
2008-02-27 15:11:45 +00:00
parent a98eb431b0
commit b5f4e61889
14 changed files with 82 additions and 321 deletions

View File

@@ -1,13 +1,14 @@
<?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>
@@ -24,7 +25,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>
@@ -33,7 +33,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>

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,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.io.Serializable;
import java.math.BigDecimal;
@@ -16,11 +16,17 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
/**
* A Hotel Booking made by a User.
*/
@Entity
public class Booking implements Serializable {
private static final long serialVersionUID = 1171567558348174963L;
private Long id;
private User user;
@@ -168,6 +174,18 @@ public class Booking implements Serializable {
this.creditCardExpiryYear = creditCardExpiryYear;
}
public void validate(MessageContext context) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1);
if (checkinDate.before(calendar.getTime())) {
context.addMessage(new MessageBuilder().source("checkinDate").defaultText(
"Check in date must be a future date").error().build());
} else if (!checkinDate.before(checkoutDate)) {
context.addMessage(new MessageBuilder().source("checkoutDate").defaultText(
"Check out date must be later than check in date").error().build());
}
}
@Override
public String toString() {
return "Booking(" + user + "," + hotel + ")";

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
* @return a list of hotels not exceeding the page size
* @param criteria the search criteria
* @return a list of hotels meeting the criteria
*/
public List<Hotel> findHotels(String searchString, int pageSize, int page);
public List<Hotel> findHotels(SearchCriteria criteria);
/**
* Find hotels by their identifier.
@@ -31,6 +29,14 @@ public interface BookingService {
*/
public Hotel findHotelById(Long id);
/**
* Returns a new booking object attached to the current persistence context.
* @param hotel the hotel being booked
* @param user the user performing the booking
* @return the booking
*/
public Booking createBooking(Hotel hotel, User user);
/**
* Cancel an existing booking.
* @param id the booking id

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;
@@ -13,6 +13,9 @@ import javax.persistence.Id;
*/
@Entity
public class Hotel implements Serializable {
private static final long serialVersionUID = 4011346719502656269L;
private Long id;
private String name;

View File

@@ -1,4 +1,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.util.List;
@@ -7,7 +7,6 @@ import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
/**
* A JPA-based implementation of the Booking Service. Delegates to a JPA entity manager to issue data access calls
@@ -33,13 +32,12 @@ public class JpaBookingService implements BookingService {
@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 criteria) {
String pattern = getSearchPattern(criteria);
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();
criteria.getPageSize()).setFirstResult(criteria.getPage() * criteria.getPageSize()).getResultList();
}
@Transactional(readOnly = true)
@@ -47,7 +45,15 @@ public class JpaBookingService implements BookingService {
return em.find(Hotel.class, id);
}
// this one is a read/write transaction
// read-write transactional methods
@Transactional
public Booking createBooking(Hotel hotel, User user) {
Booking booking = new Booking(hotel, user);
em.persist(booking);
return booking;
}
@Transactional
public void cancelBooking(Long id) {
Booking booking = em.find(Booking.class, id);
@@ -55,4 +61,15 @@ public class JpaBookingService implements BookingService {
em.remove(booking);
}
}
// helpers
private String getSearchPattern(SearchCriteria criteria) {
if (criteria.getSearchString().length() > 0) {
return "'%'" + criteria.getSearchString().toLowerCase().replace('*', '%') + "%'";
} else {
return "'%";
}
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.webflow.samples.booking.app;
package org.springframework.webflow.samples.booking;
import java.io.Serializable;
@@ -12,6 +12,9 @@ import javax.persistence.Table;
@Entity
@Table(name = "Customer")
public class User implements Serializable {
private static final long serialVersionUID = -3652559447682574722L;
private String username;
private String password;

View File

@@ -1,68 +0,0 @@
package org.springframework.webflow.samples.booking.flow.booking;
import java.util.Calendar;
import javax.persistence.EntityManager;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.webflow.action.MultiAction;
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.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 MultiAction {
/**
* 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.
*/
public Event createBooking(RequestContext context) {
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);
context.getFlowScope().put("booking", booking);
return success();
}
/**
* Perform some custom server-side validation on the flow-scoped Booking object updated by the booking form.
*
* It is expected a future milestone of Spring Web Flow 2.0 will add a Messages abstraction that decouples SWF
* artifacts from environment-specific constructs like the FacesContext.
* @param context the current flow execution request context
* @return success if validation was successful, error if not
*/
public Event validateBooking(RequestContext context) {
Booking booking = (Booking) context.getFlowScope().get("booking");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1);
if (booking.getCheckinDate().before(calendar.getTime())) {
context.getMessageContext().addMessage(
new MessageBuilder().source("checkinDate").defaultText("Check in date must be a future date")
.error().build());
return error();
} else if (!booking.getCheckinDate().before(booking.getCheckoutDate())) {
context.getMessageContext().addMessage(
new MessageBuilder().source("checkoutDate").defaultText(
"Check out date must be later than check in date").error().build());
return error();
}
return success();
}
}

View File

@@ -1,70 +0,0 @@
package org.springframework.webflow.samples.booking.flow.booking;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.model.SelectItem;
public class BookingOptions implements Serializable {
private List bedOptions;
private List smokingOptions;
private List creditCardExpMonths;
private List creditCardExpYears;
public List getBedOptions() {
if (bedOptions == null) {
bedOptions = new ArrayList();
bedOptions.add(new SelectItem(new Integer(1), "One king-size bed"));
bedOptions.add(new SelectItem(new Integer(2), "Two double beds"));
bedOptions.add(new SelectItem(new Integer(3), "Three beds"));
}
return bedOptions;
}
public List getSmokingOptions() {
if (smokingOptions == null) {
smokingOptions = new ArrayList();
smokingOptions.add(new SelectItem(Boolean.TRUE, "Smoking"));
smokingOptions.add(new SelectItem(Boolean.FALSE, "Non-Smoking"));
}
return smokingOptions;
}
public List getCreditCardExpMonths() {
if (creditCardExpMonths == null) {
creditCardExpMonths = new ArrayList();
creditCardExpMonths.add(new SelectItem(new Integer(1), "Jan"));
creditCardExpMonths.add(new SelectItem(new Integer(2), "Feb"));
creditCardExpMonths.add(new SelectItem(new Integer(3), "Mar"));
creditCardExpMonths.add(new SelectItem(new Integer(4), "Apr"));
creditCardExpMonths.add(new SelectItem(new Integer(5), "May"));
creditCardExpMonths.add(new SelectItem(new Integer(6), "Jun"));
creditCardExpMonths.add(new SelectItem(new Integer(7), "Jul"));
creditCardExpMonths.add(new SelectItem(new Integer(8), "Aug"));
creditCardExpMonths.add(new SelectItem(new Integer(9), "Sep"));
creditCardExpMonths.add(new SelectItem(new Integer(10), "Oct"));
creditCardExpMonths.add(new SelectItem(new Integer(11), "Nov"));
creditCardExpMonths.add(new SelectItem(new Integer(12), "Dec"));
}
return creditCardExpMonths;
}
public List getCreditCardExpYears() {
if (creditCardExpYears == null) {
creditCardExpYears = new ArrayList();
creditCardExpYears.add(new SelectItem(new Integer(2005), "2005"));
creditCardExpYears.add(new SelectItem(new Integer(2006), "2006"));
creditCardExpYears.add(new SelectItem(new Integer(2007), "2007"));
creditCardExpYears.add(new SelectItem(new Integer(2008), "2008"));
creditCardExpYears.add(new SelectItem(new Integer(2009), "2009"));
creditCardExpYears.add(new SelectItem(new Integer(2010), "2010"));
}
return creditCardExpYears;
}
}

View File

@@ -1,37 +0,0 @@
package org.springframework.webflow.samples.booking.flow.main;
import org.springframework.webflow.action.MultiAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.samples.booking.app.BookingService;
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 MultiAction {
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();
}
}

View File

@@ -1,109 +0,0 @@
package org.springframework.webflow.samples.booking.flow.main;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import org.springframework.util.StringUtils;
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.
*
* It is expected a future milestone of Spring Web Flow 2.0 will allow flow-scoped beans like this one to hold
* references to transient services that are restored automatically when the flow is resumed on subsequent requests.
* This would allow this SearchCriteria object to delegate to the {@link BookingService} directly, for example,
* eliminating the need for the actions in {@link MainActions}.
*/
public class SearchCriteria implements Serializable {
private static final long serialVersionUID = 1L;
/**
* The user-provided search criteria for finding Hotels.
*/
private String searchString;
/**
* The maximum page size of the Hotel result list
*/
private int pageSize = 5;
/**
* The current page of the Hotel result list.
*/
private int page;
/**
* The available page size options.
*/
private List pageSizeOptions;
/**
* Increase the current page
*/
public void nextPage() {
page++;
}
/**
* Decrease the current page
*/
public void prevPage() {
page--;
}
/**
* Signal that a find is about to occur and reset the page size to 0.
*/
public void findHotelsListener(ActionEvent event) {
page = 0;
}
public String getSearchString() {
return searchString;
}
public void setSearchString(String searchString) {
this.searchString = searchString;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public List getPageSizeOptions() {
if (pageSizeOptions == null) {
pageSizeOptions = new ArrayList();
pageSizeOptions.add(new SelectItem(new Integer(5), "5"));
pageSizeOptions.add(new SelectItem(new Integer(10), "10"));
pageSizeOptions.add(new SelectItem(new Integer(20), "20"));
}
return pageSizeOptions;
}
public String toString() {
StringBuffer criteria = new StringBuffer();
if (StringUtils.hasText(searchString)) {
criteria.append("Text: " + searchString + ", ");
}
criteria.append("Page Size: " + pageSize);
return criteria.toString();
}
}

View File

@@ -9,7 +9,7 @@
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.app.JpaBookingService" />
<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">

View File

@@ -12,10 +12,8 @@
-->
<attribute name="persistenceContext" value="true" />
<var name="bookingOptions" class="org.springframework.webflow.samples.booking.flow.booking.BookingOptions"/>
<input-mapper>
<input-attribute name="#{id}" scope="flow" />
<mapping source="#{id}" target="#{flowScope.id}" />
</input-mapper>
<start-state idref="loadHotel"/>
@@ -26,12 +24,13 @@
</action-state>
<action-state id="createBooking">
<action method="createBooking" bean="bookingActions"/>
<evaluate-action expression="#{bookingService.createBooking(hotel, user)}" result="#{flowScope.booking}"/>
<transition on="success" to="enterBookingDetails" />
</action-state>
<view-state id="enterBookingDetails" view="bookingForm.xhtml">
<transition on="proceed" to="confirmBooking">
<evaluate-action expression="booking.validate(messageContext)" />
<action method="validateBooking" bean="bookingActions" />
</transition>
<transition on="cancel" to="cancel" />

View File

@@ -6,12 +6,12 @@
<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");
@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">