• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Orbitz and Spring Webflow Case Study
 

Orbitz and Spring Webflow Case Study

on

  • 9,019 views

In this session, Michael Alford and Mark Meeker will describe the major business goals that drove the development of Orbitz Worldwide’s next generation online travel commerce platform, and how those ...

In this session, Michael Alford and Mark Meeker will describe the major business goals that drove the development of Orbitz Worldwide’s next generation online travel commerce platform, and how those goals were met with Spring and other technologies.

Last summer, Orbitz Worldwide released a new generation of its global technology platform with the goals of internationalization, white-label capability, and faster, streamlined development. Michael and Mark will describe the key challenges of this technology project and how those challenges were addressed, including the good, bad, and ugly of the Spring Framework and Spring Web Flow.

Statistics

Views

Total Views
9,019
Views on SlideShare
8,993
Embed Views
26

Actions

Likes
5
Downloads
0
Comments
0

2 Embeds 26

http://www.slideshare.net 24
http://www.slashdocs.com 2

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Orbitz and Spring Webflow Case Study Orbitz and Spring Webflow Case Study Presentation Transcript

    • Case Study: The Next Generation Online Travel Platform Michael Alford, Mark Meeker, and Alex Antonov
    • Agenda • Project context • Technology architecture • Building UI forms • Interesting Flows • Security and SSL • Progressive Enhancement • Composite View Pattern • Componentization • URLs and REST • State management and HTTP browser caching headers • Internationalization • Performance and concurrency • Summary • Q&A
    • Project Context
    • Tech History
    • 3 years of Spring Framework usage
    • Spring MVC experiments
    • Travel Commerce Platform
    • Increase developer productivity
    • Technology Architecture
    • Direct Connections Booking Services Distributed Business Travel Business Services Session Cache Thin Presentation Layer Distributed Web (Tomcat, Spring, SWF) Session Cache
    • Deals Shop Book Care (REST) (REST) (not REST) (mixed)
    • Spring and the UI Layer
    • Dear Java Developers, Whatever you do, don’t choose a web framework that generates its own XHTML (or worse, non- validating @#$% HTML that we wouldn’t be caught dead near). Your friends, The UI Team
    • <spring:bind /> <spring:bind path=”mymodel.myinput”> <input name=”${status.expression}” value=”${status.value}” /> </spring:bind>
    • Form Custom Tags • Started pre- spring-form.tld • Full control of mark-up • Abstracts concept of binding • Limit attributes available to be set • type, maxlength, class, autocomplete • Extended scope to include <label />
    • Form Custom Tags • Additional values for inputs • label-key, label-state, required, read-only • Build in hook for JavaScript • Turn-off running against live model • Augment error handling
    • Error Display • Custom Error custom tag • Formats errors with correct styles • Support for error tag debugging • Error highlighting built into form inputs
    • Interesting Flows • Leverage framework for page navigation • Large number of special conditions in booking path • Informational pop-up flows need data from flow, but don’t affect it • “Helper” flows for RESTful Ajax calls • Requirement for a “wait page”
    • Security and SSL • Browser security alerts are bad for business! • HTTP vs. HTTPS based on URL pattern • Spring Security channel filter to enforce SSL • Custom link tag insures correct protocol • Single source of truth
    • Flow listener with flow annotations to indicate flow’s security level <action-state id=“purchase”> <attribute name=“permissions” value=“USER”/> … </action-state>
    • http://blaugh.com/2006/08/21/ajax-makes-everything-better/
    • Universality • Serve pages that will work on any and every device • Fundamental basis for the web • Goes hand-in-hand with Accessibility
    • Graded Browser Support http://developer.yahoo.com/yui/articles/gbs
    • Progressive Enhancement
    • Plain Old Semantic HTML
    • Progressive Enhancement HTML CSS JS Content Presentation Behavior Frills” s it Up” it Sing” “ No “D res “M ake
    • Progressive Enhancement • Separation of Layers ( HTML / CSS / JS ) • Phased development • Easier to define view model and flows • And the “good stuff” too!
    • Progressive Enhancement 1 2 3 4 HTML     CSS     JS    
    • 1 HTML  CSS  JS 
    • <img src=”/map/image” /> <ul> <li><a href=”/map/move?dir=n”>North</a></li> <li><a href=”/map/move?dir=e”>East</a></li> <li><a href=”/map/move?dir=s”>South</a></li> <li><a href=”/map/move?dir=w”>West</a></li> ... </ul> <ul> <li><a href=”/map/zoom?level=1”>Zoom Level 1</a></li> <li><a href=”/map/zoom?level=2”>Zoom Level 2</a></li> <li><a href=”/map/zoom?level=3”>Zoom Level 3</a></li> ... </ul>
    • 2 HTML  CSS  JS 
    • 3 HTML  CSS  JS 
    • 4 HTML  CSS  JS 
    • Select from known list: Free form input:
    • 2 HTML  CSS  JS  4 HTML  CSS  JS 
    • 2 HTML  CSS  JS  4 HTML  CSS  JS 
    • XHR
    • Hijax • Term coined by Jeremy Keith • Bulletproof Ajax (http://bulletproofajax.com/) • Pull in portion of page via Ajax when XHR is supported • Re-use same portion when a full page refresh is required • Requires UI Componentization
    • Header Module 1 Rail Module 2 Footer
    • Header Login Module Module 1 JS-Enabled Rail XHR Module 2 noscript Footer Login page Module transition
    • JS-Enabled XHR no-script page transition
    • 4 HTML  CSS  JS 
    • Composite View Pattern • Separates “layout” of page from content • Allows to plug in different modules into page • Used in Apache Tiles • Leverage in-house framework • Try and gain as much re-use of JSP code
    • Componentization
    • Webapp Multi-Model • Webflow definition • Reusable components • Form
    • Reusable Component • View Module (JSP) • Model • Validator [optional] • Executor (Component Action) [optional] • Tests
    • Model = POJO
    • public class ShipperSelection implements Serializable { private List<ShipperOption> shipperOptions; private ShipperOption shipperOption; private COUNTRY country; public COUNTRY getCountry() { return country; } public void setCountry(COUNTRY country) { this.country = country; } public ShipperOption getShipperOption() { return shipperOption; } public void setShipperOption(ShipperOption shipperOption) { this.shipperOption = shipperOption; } public List<ShipperOption> getShipperOptions() { return shipperOptions; } public void setShipperOptions(List<ShipperOption> shipperOptions) { this.shipperOptions = shipperOptions; } public void addShipperOption(ShipperOption shipperOption) { this.shipperOptions.add(shipperOption); } }
    • /** * @spring.bean id=quot;shipperSelectionExecutorquot; * */ public class ShipperSelectionExecutor { private static PRODUCT_TYPE[] productTypes = {PRODUCT_TYPE.AIR}; public PRODUCT_TYPE[] getApplicableProductTypes() { return productTypes; } public boolean supports(Class clazz) { return ClassUtils.isAssignable(ShipperSelection.class, clazz); } public void setupBookComponent(ShipperSelection shipperSelection, BookState newParam) { List<ShipperOption> shipperOptions = getShipperOptions(); shipperSelection.setShipperOptions(shipperOptions); } private List<ShipperOption> getShipperOptions() { … return shipperOptions; } }
    • /** * @spring.bean id=quot;shipperSelectionValidatorquot; * */ public class ShipperSelectionValidator implements Validator { private List<COUNTRY> restrictedCountries; public ShipperSelectionValidator(List<COUNTRY> restrictedCountries) { this.restrictedCountries = restrictedCountries; } public boolean supports(Class clazz) { return ClassUtils.isAssignable(ShipperSelection.class, clazz); } public void validate(Object obj, Errors errors) { ShipperSelection c = (ShipperSelection) obj; if (restrictedCountries.contains(c.getCountry()) { errors.rejectValue(quot;countryquot;, quot;1535quot;); } } }
    • <bean id=quot;shipperSelectionExecutorquot; class=quot;com.orbitz.wl.web.book.air.action.ShipperSelectionExecutorquot; /> <bean id=quot;shipperSelectionValidatorquot; class=quot;com.orbitz.wl.web.book.air.validator.ShipperSelectionValidatorquot; /> <bean id=quot;shipperSelectionComponentquot; class=quot;com.orbitz.webframework.component.ComponentFactoryBeanquot;> <property name=quot;singletonquot; value=quot;falsequot; /> <property name=quot;idquot; value=quot;WL_UBP310.110quot; /> <property name=quot;namequot; value=quot;shipperSelectionquot; /> <property name=quot;modelClassquot; value=quot;com.orbitz.wl.web.book.air.model.ShipperSelectionquot; /> <property name=quot;executorquot; ref=quot;shipperSelectionExecutorquot;/> <property name=”validator” ref=”shipperSelectionValidator” /> </bean>
    • /** * @spring.bean id=quot;travelerInfoActionquot; * @spring.property name=quot;formObjectNamequot; value=quot;travelerInfoFormquot; * @spring.property name=quot;formObjectClassquot; *value=quot;com.orbitz.webframework.component.Formquot; * @spring.property name=quot;formObjectScopequot; value=quot;FLOWquot; * @spring.property name=quot;validatorquot; ref=quot;formValidatorquot; * @spring.property name=quot;propertyEditorRegistratorsquot; ref=quot;propertyEditorRegistratorsquot; */ public class TravelerInfoAction extends Action { private static final String SHIPPER_SELECTION = quot;shipperSelectionquot;; private static final String TRAVELERS_INPUT = quot;travelersInputquot;; @Override protected String[] getComponentIds() { return new String[] { SHIPPER_SELECTION + quot;Componentquot;, TRAVELERS_INPUT + quot;Componentquot;,}; } @Override public Event setupComponents(RequestContext context) { ShipperSelectionExecutor flightTicketExecutor = getComponentExecutor(context, SHIPPER_SELECTION); flightTicketExecutor.setupComponent(shipperSelection.getModel(), getBookState(context)); return success(); } public Event postProcess(RequestContext context) { TravelersInput travelersInput = getComponentModel(context, TRAVELERS_INPUT); TravelersInputExecutor whosTravelingExecutor = getComponentExecutor(context, TRAVELERS_INPUT); whosTravelingExecutor.executePostProcess(travelersInput, getBookState(context)); return success(); } }
    • URLs and REST
    • Fundamental nature of the web
    • RESTful URL represents all the state necessary to reference a particular resource
    • long-lived backwards compatible
    • benefits?
    • usability
    • agility and runtime isolation
    • Cache: performance and hardware savings
    • exponential growth of network effects
    • diffusion of innovation
    • search engine optimization
    • implementation?
    • Spring Webflow versus Spring MVC
    • one framework to rule them all
    • Session scope obfuscated public interface Action { public Event execute(RequestContext context) throws Exception; } public interface RequestContext { public MutableAttributeMap getRequestScope(); public MutableAttributeMap getFlashScope(); public MutableAttributeMap getFlowScope(); public MutableAttributeMap getConversationScope(); … } requestContext.getExternalContext().getSessionMap()
    • RESTful stateless flows
    • Deals Shop Book Care (REST) (REST) (not REST) (mixed)
    • <flow …> <attribute name=”stateless” value=”true”/> … </flow>
    • no view state no pause no continuation enforce with customization at XMLFlowBuilder level & made easier by SWF-310
    • separate model from the URL
    • http://www.ebookers.com/shop/home? type=air&ar.type=roundTrip&ar.rt.leaveSlice.orig.key=L HR&ar.rt.leaveSlice.dest.key=FLL&ar.rt.leaveSlice.date =12%2F12%2F07&ar.rt.leaveSlice.time=Anytime&ar.rt.retu rnSlice.date=15%2F12%2F07&ar.rt.returnSlice.time=Anyti me&ar.rt.numAdult=1&ar.rt.numSenior=0&ar.rt.numChild=0 &ar.rt.child%5B0%5D=&ar.rt.child%5B1%5D=&ar.rt.child %5B2%5D=&ar.rt.child%5B3%5D=&ar.rt.child %5B4%5D=&ar.rt.child%5B5%5D=&ar.rt.child %5B6%5D=&ar.rt.child%5B7%5D=&search=Search+Flights Internet Explorer’s URL limit: 2083 characters source: Source: http://support.microsoft.com/kb/208427
    • input mapper
    • AttributeMapper config <flow …> … <subflow-state id=”executeSearch” flow=”search”/> <attribute-mapper> <input-mapper> <input-attribute name=”dealsModel.searchAttr1”/> <input-attribute name=”dealsModel.searchAttr2”/> </input-mapper> </attribute-mapper> <transition on=”someOutcome” to=”someView”/> </subflow-state> </flow>
    • SWF-297 will also allow the input-mapper to also record validation error messages, providing a better alternative to the FormAction generally used for this purpose now.
    • re-use flows as subflows without changing the URL
    • State Management
    • infinite memory
    • used smallest scope possible
    • avoided session and conversation usage
    • request scope not for redirect-after-post
    • flow and flash scopes
    • continuation limits <flow:executor id=quot;flowExecutorquot; registry-ref=quot;flowRegistryquot;> <flow:repository type=quot;continuationquot; max-conversations=quot;1quot; max-continuations=quot;30quot;/> </flow:executor>
    • stateless flows, continuations, and form value history
    • no-cache no-store
    • Spring source when cacheSeconds == 0 /** * Prevent the response from being cached. * See www.mnot.net.cache docs. */ protected final void preventCaching(HttpServletResponse response) { response.setHeader(HEADER_PRAGMA, quot;No-cachequot;); if (this.useExpiresHeader) { // HTTP 1.0 header response.setDateHeader(HEADER_EXPIRES, 1L); } if (this.useCacheControlHeader) { // HTTP 1.1 header: quot;no-cachequot; is the standard value, // quot;no-storequot; is necessary to prevent caching on FireFox response.setHeader(HEADER_CACHE_CONTROL, quot;no-cachequot;); response.addHeader(HEADER_CACHE_CONTROL, quot;no-storequot;); } }
    • workaround #1 <bean name=quot;dealsFlowControllerquot; class=quot;org.springframework.webflow.…quot;> … <property name=quot;cacheSecondsquot; value=quot;1quot;/> </bean>
    • workaround #2 <bean name=quot;dealsFlowControllerquot; class=quot;org.springframework.webflow.…quot;> … <property name=“useCacheControlHeaderquot; value=“falsequot;/> <property name=quot;cacheSecondsquot; value=“0quot;/> </bean>
    • Internationalization
    • third-party libraries
    • Joda-time (what JDK Date/Calendar/DateFormat should be)
    • International Components for Unicode (ICU)
    • JScience’s Units JSR-275
    • immutable / thread-safe domain models & formatters
    • point-of-sale and locale parameterization
    • URL pattern POS
    • POS defaultLocale
    • POS config <pos> <posCode>EBCH</posCode> <description>Ebookers Switzerland Point of Sale</description> … <supportedLocales> <defaultLocale>de_CH</defaultLocale> <locale>de_CH</locale> <locale>fr_CH</locale> … </supportedLocales> … </pos>
    • locale selection
    • <property name=quot;interceptorsquot;> <list> … <bean id=quot;localeChangeInterceptorquot; class=“ localeChangeInterceptorquot;> org.springframework.web.i18n. <property name=quot;paramNamequot;> <value>locale</value> </property> </bean> … </list> <property>
    • locale persistence <bean id=quot;localeResolverquot; class=quot;org.springframework.web.servlet.i18n.cookieLocaleResolverquot;/> OR <bean id=quot;localeResolverquot; class=quot;org.springframework.web.servlet.i18n.sessionLocaleResolverquot;/>
    • <spring:bind /> versus <format />
    • register PropertyEditors editors.put(Currency.class, new CurrencyEditor()); editors.put(Scalar.class, new ScalarEditor()); editors.put(DateTime.class, new JodaDateTimeEditor(true)); editors.put(YearMonthDay.class, new JodaYearMonthDayEditor(true)); editors.put(LocalDate.class, new JodaLocalDateEditor(true)); …
    • format tags for data types format:dateTime format:distance format:distanceVector format:interval format:money format:number format:period format:address format:creditCard format:emailAddress format:location format:name format:phoneNumber
    • content access
    • MessageSource <bean id=quot;messageSource“ class=quot;com.orbitz.webframework.ContentMessageSourcequot;> <constructor-arg> <ref bean=quot;contentMessageConfigquot;/> </constructor-arg> <property name=quot;useCodeAsDefaultMessagequot; value=quot;truequot;/> </bean>
    • content access • <format:message /> • supports tokens of custom type
    • Spring’s dependency on JDK’s MessageFormat
    • open-source
    • performance and concurrency
    • good performance from Spring & Spring Web Flow
    • lock contention quot;http-8585-Processor39quot; daemon prio=1 tid=0x081634c8 nid=0x65b7 waiting for moni tor entry [0x9d41a000..0x9d41e0b0] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistr y.getSingleton(DefaultSingletonBeanRegistry.java:114) - waiting to lock <0xb0d85828> (a java.util.LinkedHashMap) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:187) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:156)
    • cached results of Spring’s getBeansOfType (circa March 2007 – Juergen says it may be fixed in Spring v2.0.5)
    • SWF, registerCustomEditors, PropertyEditor issues
    • Other lock contentions • JDK RMIClassLoaderSpi • JDK java.security.* • Tomcat • 3000 tag invocations in search results page view • Log4j • ICU
    • ConcurrentHashMap JDK 1.5 Emory backport for JDK 1.4
    • concurrency is hard (see Rob Harrop’s “Practical Enterprise Concurrency” presentation)
    • Scala: JVM language Erlang-style message passing
    • Summary • Project context • Technology architecture • Building UI forms • Managing Flows • Security and SSL • Progressive Enhancement • Composite View Pattern • Componentization • URLs and REST • State management and HTTP browser caching headers • Internationalization • Performance and concurrency • Summary • Q&A
    • Slides & Contact Slides: http://markmeeker.com/events/tse2007 Emails: Michael Alford - malford@orbitz.com Mark Meeker - mmeeker@orbitz.com We are Hiring: http://www.orbitz.com/startmycareer