Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Muster in Webcontrollern

666 views

Published on

XPDays 2008, Hamburg

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Muster in Webcontrollern

  1. 1. MUSTER IN WEBCONTROLLERN arthur tomas jens himmelreich
  2. 2. VIEW CONTROLLER MODEL
  3. 3. VIEW CONTROLLER MODEL
  4. 4. VIEW CONTROLLER MODEL
  5. 5. EXKURS
  6. 6. VIEW CONTROLLER MODEL
  7. 7. WebMVC
  8. 8. FUNK TION
  9. 9. HYGI ENE
  10. 10. VIEW CONTROLLER MODEL
  11. 11. CONTROLLER
  12. 12. CONTROLLER public static void main(String[] arguments) { if(basket.isPaymentCreditCard()) { if( customerService.isCreditCardLock(bask log.info("CreditCard isLocked"); return new ModelAndView(urlComposer.g } CreditCardOrder order = creditCardOrder if(isBankAuthenticationResponse(request)) log.debug("Bank response request with credtitCartService.finalizeAuthentifi } else { AuthentificationIntermediateResponse = creditCardService.beginAuthenti urlComposer.getSslUrl("/Bes if (authResponse.needs5dsecureAuthent log.debug("CreditCard needs 5dsec return show5dSecurePage(authRespo } } if (creditCardService.isAuthenticationErr log.info("creditCardService.isAuthent return new ModelAndView(urlComposer.g } else { log.debug("creditCardService without creditCardService.authorizePayment(ca
  13. 13. CONTROLLER
  14. 14. CONTROLLER ?
  15. 15. KOMPLEXES SETUP
  16. 16. REQUEST RESPONSE
  17. 17. EXPERIMENT
  18. 18. if(basket.isPaymentCreditCard()) { if( customerService.isCreditCardLock(basket.getCardNumber()) ) { log.info("CreditCard isLocked"); return new ModelAndView(urlComposer.getSslRedirectUrl("/Zahlungs } CreditCardOrder creditCardOrder = creditCardOrderFactory.newPoseidonOrder( if(isBankAuthenticationResponse(request)) { log.debug("Bank response request with: CREDITCARD_5dSECURE_BANK_ creditCardService.finalizeAuthentification(get5dSecureBankRespon } else { AuthentificationIntermediateResponse authResponse = poseidonService.beginAuthentification(poseidonOrder, urlComposer.getSslUrl("/Bestell.html?submit1 if (authResponse.needs5dsecureAuthentication()) { log.debug("CreditCard needs 5dsecureAuthentication!"); return show5dSecurePage(authResponse); } } if (creditCardService.isAuthenticationError(creditCardOrder)) { log.info("creditCardService.isAuthenticationError"); return new ModelAndView(urlComposer.getSslRedirectUrl("/Zahl } else { log.debug("creditCardService without AuthenticationError"); creditCardService.authorizePayment(creditCardOrder); if (creditCardService.isSuccessfullAuthorized(creditCardOrder)) log.debug("creditCardService.isSuccessfullAuthorized"); basket.getPaymentMethod().setCardNumber(creditCardOrder.getA
  19. 19. CONTROLLER
  20. 20. REFACTORING
  21. 21. CLEAN
  22. 22. REZ EPT
  23. 23. 1
  24. 24. 2
  25. 25. 3
  26. 26. 4
  27. 27. 5 TE ST
  28. 28. 6
  29. 29. 7
  30. 30. 8 TE ST
  31. 31. CLEAN
  32. 32. OBJEKTE ERKENNEN
  33. 33. PARAMETER OBJEKT
  34. 34. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = (PaymentRule)paymentRuleComposer.compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  35. 35. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = paymentRuleComposer .compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  36. 36. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = paymentRuleComposer .compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc); Session session = modelBuilde Basket basket = modelBuilder. Customer customer = modelBuil PaymentRule paymentRule = pay
  37. 37. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = (PaymentRule)paymentRuleComposer.compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  38. 38. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = (PaymentRule)paymentRuleComposer.compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  39. 39. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = (PaymentRule)paymentRuleComposer.compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc); (session, basket, modelBuilder)
  40. 40. public class SessionContext { private ModelBuilder modelBuilder; private PaymentRule paymentRule; private boolean needsUpdate; public SessionContext( ModelBuilder modelBuilder, RuleComposer rulecomposer) { this.needsUpdate = false; this.modelBuilder = modelBuilder; this.paymentRule = createPaymentRule(rulecomposer); } // ... }
  41. 41. public class SessionContext { private ModelBuilder modelBuilder; private PaymentRule paymentRule; private boolean needsUpdate; public SessionContext( ModelBuilder modelBuilder, RuleComposer rulecomposer) { this.needsUpdate = false; this.modelBuilder = modelBuilder; this.paymentRule = createPaymentRule(rulecomposer); } // ... }
  42. 42. public class SessionContext { private ModelBuilder modelBuilder; private PaymentRule paymentRule; private boolean needsUpdate; public SessionContext( ModelBuilder modelBuilder, RuleComposer rulecomposer) { this.needsUpdate = false; this.modelBuilder = modelBuilder; this.paymentRule = createPaymentRule(rulecomposer); } // ... } SessionContext( ModelBuilder modelBuilder, RuleComposer rulecomposer)
  43. 43. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = (PaymentRule)paymentRuleComposer.compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  44. 44. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = (PaymentRule)paymentRuleComposer.compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  45. 45. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext( modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(context); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(context); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  46. 46. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(context); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc); SessionContext context = new SessionContext( modelBuilder, paymentRuleComposer);
  47. 47. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(context); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(context); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc); disableDesired- DeliveryDate(context);
  48. 48. ECHTES OBJEKT
  49. 49. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(context); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(context); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  50. 50. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(context); if(escapeModelAndView != null) { return escapeModelAndView; } context.disableDesiredDeliveryDate(); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  51. 51. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(context); if(escapeModelAndView != null) { return escapeModelAndView; } context.setDesiredDeliveryDate(); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc); context. disableDesiredDeliveryDate();
  52. 52. METHODEN OBJEKT
  53. 53. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(context); if(escapeModelAndView != null) { return escapeModelAndView; } context.disableDesiredDeliveryDate(); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  54. 54. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(context); if(escapeModelAndView != null) { return escapeModelAndView; } context.disableDesiredDeliveryDate(); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc);
  55. 55. @Override protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } context.disableDesiredDeliveryDate(); CountryCode cc = catalogService.getCountryCodeByIsoCode2(basket.getCountryToDeliver()); logInfos(basket); putDataToModel(modelBuilder, basket, cc); getMavIfStateIsNot- ValidForCheckout(context)
  56. 56. private ModelAndView getMavIfStateIsNotValidForCheckout( SessionContext context) { if( !context.getBasket().hasLineItems() ) { return new ModelAndView(redirectCartView); } if(!context.getBasket().hasInvoiceAddress()) { return new ModelAndView(redirectInvoiceView); } if(!context.getBasket().isValidForCheckout(paymentRule)) { if( !context.getBasket().isFurnitureOk() || !context.getBasket().isPaymentMethodAllowed( context.getPaymentRule()) || !context.getBasket().isPaymentMethodValid()) { return new ModelAndView(redirectInvoiceAndDeliveryView); } return new ModelAndView(redirectInvoiceView); } return null; }
  57. 57. private ModelAndView getMavIfStateIsNotValidForCheckout( SessionContext context) { if( !context.getBasket().hasLineItems() ) { return new ModelAndView(redirectCartView); } if(!context.getBasket().hasInvoiceAddress()) { return new ModelAndView(redirectInvoiceView); } if(!context.getBasket().isValidForCheckout(paymentRule)) { if( !context.getBasket().isFurnitureOk() || !context.getBasket().isPaymentMethodAllowed( context.getPaymentRule()) || !context.getBasket().isPaymentMethodValid()) { return new ModelAndView(redirectInvoiceAndDeliveryView); } return new ModelAndView(redirectInvoiceView); } return null; }
  58. 58. private ModelAndView getMavIfStateIsNotValidForCheckout( SessionContext context) { if( !context.getBasket().hasLineItems() ) { return new ModelAndView(redirectCartView); } if(!context.getBasket().hasInvoiceAddress()) { return new ModelAndView(redirectInvoiceView); } if(!context.getBasket().isValidForCheckout(paymentRule)) { if( !context.getBasket().isFurnitureOk() || !context.getBasket().isPaymentMethodAllowed( context.getPaymentRule()) || !context.getBasket().isPaymentMethodValid()) { return new ModelAndView(redirectInvoiceAndDeliveryView); } return new ModelAndView(redirectInvoiceView); } return null; } !context.get !context.get context.get !context.get
  59. 59. public CheckOutState(Basket basket, PaymentRule rule) { this.basket = basket; this.rule = rule; } public CheckOutStep nextStep() { if (basket.isEmpty()) return CART; if (basket.isValidForCheckout(rule)) return LAST_CHECK; if ((!basket.hasInvoiceAddress()) || (basket.isFurnitureOk() && basket.isPaymentMethodAllowed(rule) && basket.isPaymentMethodValid())) return INVOICE_ADDRESS; return PAYMENT_AND_DELIVERY; }
  60. 60. public CheckOutState(Basket basket, PaymentRule rule) { this.basket = basket; this.rule = rule; } public CheckOutStep nextStep() { if (basket.isEmpty()) return CART; if (basket.isValidForCheckout(rule)) return LAST_CHECK; if ((!basket.hasInvoiceAddress()) || (basket.isFurnitureOk() && basket.isPaymentMethodAllowed(rule) && basket.isPaymentMethodValid())) return INVOICE_ADDRESS; return PAYMENT_AND_DELIVERY; } CheckOutState( Basket basket, PaymentRule rule)
  61. 61. private ModelAndView handleOrderRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); CheckOutState state = new CheckOutState( context.getBasket(), context.getPaymentRule()); if (!state.isValidForCheckOut()) return modelAndViewFor(state.nextStep()); context.disableDesiredDeliveryDate();
  62. 62. private ModelAndView handleOrderRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); CheckOutState state = new CheckOutState( context.getBasket(), context.getPaymentRule()); if (!state.isValidForCheckOut()) return modelAndViewFor(state.nextStep()); context.disableDesiredDeliveryDate();
  63. 63. private ModelAndView handleOrderRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); CheckOutState state = new CheckOutState( context.getBasket(), context.getPaymentRule()); if (!state.isValidForCheckOut()) return modelAndViewFor(state.nextStep()); context.disableDesiredDeliveryDate(); state = new CheckOutState( context.getBasket(), context.getPaymentRule());
  64. 64. protected ModelAndView handleSslRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { if( isNotAuthorizised(request, modelBuilder)){ return handleLogin(request, response, modelBuilder); } Session session = modelBuilder.getSession(); Basket basket = modelBuilder.getBasket(); Customer customer = modelBuilder.getCustomer(); PaymentRule paymentRule = (PaymentRule)paymentRuleComposer.compose(basket, customer); ModelAndView escapeModelAndView = getMavIfStateIsNotValidForCheckout(basket, paymentRule, session); if(escapeModelAndView != null) { return escapeModelAndView; } disableDesiredDeliveryDate(session, basket, modelBuilder);
  65. 65. private ModelAndView handleOrderRequest(HttpServletRequest request, HttpServletResponse response, ModelBuilder modelBuilder) throws Exception { SessionContext context = new SessionContext(modelBuilder, paymentRuleComposer); CheckOutState state = new CheckOutState( context.getBasket(), context.getPaymentRule()); if (!state.isValidForCheckOut()) return modelAndViewFor(state.nextStep()); context.disableDesiredDeliveryDate();
  66. 66. ER GO
  67. 67. OBJEKT ≠DING
  68. 68. OBJEKT = DING
  69. 69. OBJEKT ≠ DING
  70. 70. SEIHEB AMME
  71. 71. OBJEKTE ERKENNEN
  72. 72. OBJEKTE GEBÄREN
  73. 73. OBJEKTE PFLEGEN
  74. 74. OBJEKT ≠DING SEIHEB AMME
  75. 75. EN DE
  76. 76. CREDITS von tangaroo von Axel Pfaender von binaryCoco von tilaneseven von spiffie von static416 von autowitch von Daveybot von woodleywonderworks von Tim Morgan von Tim Morgan von Tim Morgan von nickwheeleroz von otisarchives1 von *ejk* von cuulongden von Laure Wayaffe von mab @ flickr von Kazze von aesop von SteveMcN von canonsnapper von bies

×