View Javadoc

1   package org.paneris.jammyjoes.shopping;
2   
3   import java.util.Enumeration;
4   import java.util.Iterator;
5   import java.util.LinkedList;
6   import java.util.List;
7   
8   import javax.servlet.ServletConfig;
9   import javax.servlet.ServletException;
10  
11  import org.melati.Melati;
12  import org.melati.servlet.Form;
13  import org.melati.poem.Field;
14  import org.melati.servlet.InvalidUsageException;
15  import org.melati.PoemContext;
16  import org.melati.servlet.PathInfoException;
17  import org.melati.servlet.TemplateServlet;
18  import org.melati.template.ServletTemplateContext;
19  import org.melati.util.InstantiationPropertyException;
20  import org.melati.util.MelatiException;
21  import org.paneris.jammyjoes.model.JammyjoesDatabase;
22  import org.paneris.jammyjoes.mvp.CatalogueEntryPresenter;
23  import org.paneris.jammyjoes.mvp.Context;
24  import org.paneris.jammyjoes.mvp.ContextWrapper;
25  import org.paneris.jammyjoes.mvp.Presenter;
26  import org.paneris.jammyjoes.util.JammyJoesContextUtil;
27  import org.paneris.jammyjoes.util.JammyJoesUtil;
28  
29  
30  /** 
31   * The Melati Shopping Trolley Interface is designed to provide 
32   * a flexible adstraction of the basic processes of allowing 
33   * customers to buy goods on a website. 
34   * By default, it is not integrated with POEM, and so makes 
35   * no assumptions about
36   * the underlying database implementaion.
37   *
38   * Please read the readme.txt file for full instructions.
39   *
40   * The Trolley is a servlet that handles the user's interaction with 
41   * the Shopping Trolley.
42   *
43   * @see org.paneris.melati.shopping.ShoppingTrolley
44   * @see org.paneris.melati.shopping.ShoppingTrolleyItem
45   * @see org.paneris.melati.shopping.DefaultShoppingTrolley
46   * @see org.paneris.melati.shopping.DefaultShoppingTrolleyItem
47   *
48   **/
49  
50  public class Trolley extends TemplateServlet {
51  
52    private static final long serialVersionUID = 1L;
53  
54    public MelatiShoppingConfig config;
55     
56    /**
57     * Inititialise the Shopping Trolley Engine.  This will load a file called
58     * org.paneris.melati.shopping.ShoppingTrolley.properties in order to 
59     * find the classes that implement this shopping implementation
60     *
61     * @param conf - the Servlet's config parameters
62     * @see org.paneris.melati.shopping.MelatiShoppingConfig
63     **/
64  
65    public void init(ServletConfig conf ) throws ServletException
66    {
67       super.init(conf );
68       try {
69         config = new MelatiShoppingConfig();
70       } catch (MelatiException e) {
71         throw new ServletException(e.toString());
72       }
73    }
74  
75  
76    public String getSysAdminName() {
77      return "Tim Joyce";
78    }
79    public String getSysAdminEmail() {
80      return "timj@hoop.co.uk";
81    }
82  
83    /**
84     * Main entry point for this servlet
85     *
86     * @param melati - the melati for this request
87     * @param context - the Template Context for this request
88     *
89     * @return - the name of the template to be returned to the user
90     *
91     * @throws InvalidUsageException - if this request has an invalid form
92     **/
93  
94    protected String doTemplateRequest(Melati melati, ServletTemplateContext context)
95        throws Exception {
96  
97      if (config==null) 
98         throw new ShoppingConfigException("Shopping Trolley not Configured");
99      JammyjoesDatabase db = (JammyjoesDatabase) melati.getDatabase();
100     context.put("menutypes", db.getTypeTable().selection());
101     context.put("jjutil", new JammyJoesContextUtil());
102     if (JammyJoesUtil.telesales(melati)) assertLogin(melati);
103     ShoppingContext shoppingContext = (ShoppingContext)melati.getPoemContext();
104     if (shoppingContext.getMethod().equals("Load")) 
105       return Load(melati, shoppingContext.stid);
106     if (shoppingContext.getMethod().equals("View")) return View(melati);
107     if (shoppingContext.getMethod().equals("Update")) return Update(melati);
108     if (shoppingContext.getMethod().equals("Add")) 
109       return Add(melati, shoppingContext.stid, shoppingContext.quantity);
110     if (shoppingContext.getMethod().equals("MultipleAdd")) 
111       return MultipleAdd(melati);
112     if (shoppingContext.getMethod().equals("Remove")) 
113       return Remove(melati, shoppingContext.stid);
114     if (shoppingContext.getMethod().equals("Set")) 
115       return Set(melati, shoppingContext.stid, shoppingContext.quantity);
116     if (shoppingContext.getMethod().equals("Details")) return Details(melati);
117     if (shoppingContext.getMethod().equals("Confirm")) return Confirm(melati);
118     if (shoppingContext.getMethod().equals("Paid")) return Paid(melati);
119     if (shoppingContext.getMethod().equals("Abandon")) return Abandon(melati);
120     if (shoppingContext.getMethod().equals("CatalogueEntry")) return CatalogueEntry(melati);
121     throw new InvalidUsageException(this, shoppingContext);
122   }
123 
124   /** 
125    * load the trolley from something persistent
126    *
127    * @param melati - the melati for this request
128    * @param id - an id that can be used to identify the trolley to be loaded
129    *
130    * @return - "Trolley" - the page where users manipulate their 
131    *           Shopping Trolley
132    *
133    * @throws InstantiationPropertyException - if we cannot construct trolley
134    **/
135 
136   protected String Load(Melati melati, Integer id)
137    throws InstantiationPropertyException {
138     ShoppingTrolley trolley = ShoppingTrolley.newTrolley(config);
139     trolley.initialise(melati,config,id);
140     melati.getTemplateContext().put("trolley", trolley);
141     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
142   }
143 
144 
145   /** 
146    * load the trolley from something persistent
147    *
148    * @param melati - the melati for this request
149    *
150    * @return - "Trolley" - the page where users manipulate their 
151    *           Shopping Trolley
152    *
153    * @throws InstantiationPropertyException - if we cannot construct trolley
154    **/
155 
156   protected String Save(Melati melati)
157    throws InstantiationPropertyException {
158     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
159     trolley.save();
160     melati.getTemplateContext().put("trolley", trolley);
161     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
162   }
163 
164   /** 
165    * view the trolley
166    *
167    * @param melati - the melati for this request
168    *
169    * @return - "Trolley" - the page where users manipulate their 
170    *           Shopping Trolley
171    *
172    * @throws InstantiationPropertyException - if we cannot construct the trolley
173    **/
174   protected String CatalogueEntry(Melati melati)
175    throws InstantiationPropertyException {
176      JammyjoesDatabase db = (JammyjoesDatabase) melati.getDatabase();
177      Context context = new ContextWrapper(melati.getServletTemplateContext());
178      Presenter presenter = new CatalogueEntryPresenter(db.getProductTable(),context);
179      presenter.handleInteraction(presenter.createSelection(), presenter.createCommand());
180      context.put("type", new Field(null, db.getProductTable().getTypeColumn()));
181      context.put("jjutil", new JammyJoesContextUtil());
182      context.put("trolley", ShoppingTrolley.getInstance(melati,config));
183      context.put("ages", db.getAgeTable().selection());
184      return JammyJoesUtil.shoppingTemplate(melati, "CatalogueEntry");
185   }
186 
187 
188   /** 
189    * view the trolley
190    *
191    * @param melati - the melati for this request
192    *
193    * @return - "Trolley" - the page where users manipulate their 
194    *           Shopping Trolley
195    *
196    * @throws InstantiationPropertyException - if we cannot construct the trolley
197    **/
198   protected String View(Melati melati)
199    throws InstantiationPropertyException {
200     melati.getTemplateContext().put("trolley", ShoppingTrolley.getInstance(melati,config));
201     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
202   }
203 
204   /** 
205    * update the entire trolley, changing quantities
206    * and removing items.  the POSTed form is analysed for fields with names of 
207    * the form:
208    *
209    * trolleyitem_<item id>_quantity - the new quantity of the item (if set)
210    * trolleyitem_<item id>_deleted - remove this item from the trolley (if set)
211    *
212    * items will also be deleted if the quantity is set to 0
213    *
214    * @param melati - the melati for this request
215    *
216    * @return - "Trolley" - the page where users manipulate their 
217    *           Shopping Trolley
218    *
219    * @throws InstantiationPropertyException - if we cannot construct the trolley
220    */
221   protected String Update(Melati melati) throws InstantiationPropertyException {
222     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
223     List removeItems = new LinkedList();
224     for (Enumeration c = trolley.getItems(); c.hasMoreElements();) {
225       ShoppingTrolleyItem item = (ShoppingTrolleyItem)c.nextElement();
226       String formName = "trolleyitem_" + item.getId();
227       String formQuantity = formName + "_quantity";
228       String formDeleted = formName + "_deleted";
229       String deleted = 
230              Form.getFormNulled(melati.getServletTemplateContext(),formDeleted);
231       String quantity = 
232              Form.getFormNulled(melati.getServletTemplateContext(),formQuantity);
233       System.err.println(deleted + " " + quantity);
234       if (deleted != null || quantity == null || quantity.equals("0")) {
235         removeItems.add(item);
236       } else {
237         item.setQuantity(new Integer(quantity));
238       }
239       
240     }
241     Iterator removeItemsIterator = removeItems.iterator();
242     while (removeItemsIterator.hasNext()) {
243         ShoppingTrolleyItem element = (ShoppingTrolleyItem) removeItemsIterator.next();
244       trolley.removeItem(element);
245       }
246     melati.getTemplateContext().put("trolley",trolley);
247     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
248   }
249 
250   /** 
251    * add multiple items to the trolley, 
252    * or add to the quantities already in the 
253    * trolley.  
254    * The POSTed form is analysed for fields with names of 
255    * the form:
256    *
257    * product_<item id> - the id of the item to be added
258    * quantity_<item id> - the quantity to add
259    *
260    * If no quantity is set, a single item will be added.
261    *
262    * @param melati - the melati for this request
263    *
264    * @return - "Trolley" - the page where users manipulate their 
265    *           Shopping Trolley
266    *
267    * @throws InstantiationPropertyException - if we cannot construct trolley
268    **/
269 
270   protected String MultipleAdd(Melati melati)
271    throws InstantiationPropertyException {
272     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
273     for (Enumeration e = melati.getRequest().getParameterNames(); 
274                      e.hasMoreElements();) {
275       String name = (String)e.nextElement();
276       if (name.length() > 8) {
277         String p = name.substring(0,7);
278         if (p.equals("product")) {
279           String id = name.substring(8);
280           Integer idInt = new Integer(id);
281           ShoppingTrolleyItem item = trolley.getItem(idInt);
282           String quantityName = "quantity_" + id;
283           String priceName = "price_" + id;
284           String descriptionName = "description_" + id;
285           Integer quantity = new Integer(1);
286           Double price = null;
287           String quantitySring = 
288                  Form.getFormNulled
289                  (melati.getServletTemplateContext(), quantityName);
290           String priceString = 
291                  Form.getFormNulled
292                  (melati.getServletTemplateContext(), priceName);
293           String description = 
294                  Form.getFormNulled
295                  (melati.getServletTemplateContext(), descriptionName);
296           if (quantitySring != null) 
297             quantity = new Integer(quantitySring);
298           if (priceString != null) price = new Double(priceString);
299           if (item == null) {
300             item = newItem(trolley,idInt,price,description);
301           }
302           item.setQuantity(new Integer(item.getQuantity().intValue() + quantity.intValue()));
303         }
304       }
305     }
306     melati.getTemplateContext().put("trolley",trolley);
307     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
308   }
309 
310   /** 
311    * add a single item to the trolley, or add to the quantity already in the 
312    * trolley.  the product is specified on the pathinfo which should be of the
313    * form:
314    *
315    * /<logicaldatabase>/<id>/<quantity>/Add/
316    *
317    * if no quantity is set, a single item will be added.  The form parmaeters
318    * will be parsed to see if they contain "price" and/or "description" fields.
319    * if they are present, they will be used to set up the item
320    *
321    * @param melati - the melati for this request
322    * @param id - the id of the item to be added
323    *
324    * @return - "Trolley" - the page where users manipulate their 
325    *           Shopping Trolley
326    *
327    * @throws InstantiationPropertyException - if we cannot construct trolley
328    **/
329 
330   protected String Add(Melati melati, Integer id, Integer quantity)
331    throws InstantiationPropertyException {
332      System.err.println("Adding");
333     // the quantity is defaulted to 1, so if you don't set it you will get one
334     if (quantity == null || quantity.intValue() == 0) quantity = new Integer(1);
335     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
336     ShoppingTrolleyItem item = trolley.getItem(id);
337     if (item == null) {
338       Double price = null;
339       String priceString = 
340              Form.getFormNulled(melati.getServletTemplateContext(), "price");
341       if (priceString != null) price = new Double(priceString);
342       item = newItem(trolley,id,price,
343           Form.getFormNulled(melati.getServletTemplateContext(), "description"));
344     }    
345     Integer currentQuantity = item.getQuantity();
346     if (currentQuantity == null) currentQuantity = new Integer(0);
347     item.setQuantity(new Integer(currentQuantity.intValue() + quantity.intValue()));
348     melati.getTemplateContext().put("trolley",trolley);
349     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
350   }
351 
352   /** 
353    * remove a single item from the trolley, the product is specified on the 
354    * pathinfo which should be of the form:
355    *
356    * /<logicaldatabase>/<id>/Remove/
357    *
358    * @param melati - the melati for this request
359    *
360    * @return - "Trolley" - the page where users manipulate their 
361    *           Shopping Trolley
362    *
363    * @throws InstantiationPropertyException - if we cannot construct trolley
364    **/
365 
366   protected String Remove(Melati melati, Integer id)
367    throws InstantiationPropertyException {
368     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
369     ShoppingTrolleyItem item = trolley.getItem(id);
370     trolley.removeItem(item);
371     melati.getTemplateContext().put("trolley",trolley);
372     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
373   }
374 
375   /** 
376    * set the quantity of an item in the trolley, 
377    * the product and new quantity is
378    * specified on the pathinfo which should be of the form:
379    *
380    * /<logicaldatabase>/<id>/<quantity>/Set/
381    *
382    * @param melati - the melati for this request
383    * @param id - the id of the item to be removed
384    *
385    * @return - "Trolley" - the page where users manipulate their 
386    *           Shopping Trolley
387    *
388    * @throws InstantiationPropertyException - if we cannot construct trolley
389    **/
390 
391   protected String Set(Melati melati, Integer id, Integer quantity)
392    throws InstantiationPropertyException {
393     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
394     ShoppingTrolleyItem item = trolley.getItem(id);
395     if (item == null) item = newItem(trolley,id, null, null);
396     item.setQuantity(quantity);
397     melati.getTemplateContext().put("trolley",trolley);
398     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
399   }
400   
401   /** 
402    * return the page where the user enters their details
403    *
404    * @param melati - the melati for this request
405    *
406    * @return - "Details" - the page where users enter their details
407    *
408    * @throws InstantiationPropertyException - if we cannot construct trolley
409    **/
410 
411   protected String Details(Melati melati)
412    throws InstantiationPropertyException {
413     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
414     trolley.setDefaultDetails(melati);
415     melati.getTemplateContext().put("trolley",trolley);
416     return JammyJoesUtil.shoppingTemplate(melati, "Details");
417   }
418   
419   /** 
420    * update the user's information and return the
421    * confirmation page
422    *
423    * @param melati - the melati for this request
424    *
425    * @return - "Confirm" - the page where users confirm their order
426    *
427    * @throws InstantiationPropertyException - if we cannot construct trolley
428    */
429   protected String Confirm(Melati melati)
430    throws InstantiationPropertyException {
431     JammyJoesShoppingTrolley trolley = (JammyJoesShoppingTrolley)ShoppingTrolley.getInstance(melati,config);
432     if (Form.getFormNulled(melati.getServletTemplateContext(),"submittoken") != null) 
433       trolley.setFromForm(melati);
434     trolley.save();
435     melati.getServletTemplateContext().put("trolley",trolley);
436     return JammyJoesUtil.shoppingTemplate(melati, "Confirm");
437   }
438   
439   /** 
440    * complete the user's shopping experience, and remove their Shopping Trolley
441    * from the Session
442    *
443    * If you need to do something (like send an email) following confirmation 
444    * of payment, define the method in <Your>ShoppingTrolley.java:
445    * 
446    *  public void confirmPayment(Melati melati) {}
447    *
448    * Because the callback request (typically) comes from the Payment Server, 
449    * you will not have the user's shoping trolley (Session) available to them.
450    * You will therefore need to get whatever information you require from 
451    * something persistent.
452    *
453    * The alternative is to get the Payment Server to generate the emails (or 
454    * whatever) for you.  Most Payment Servers offer this facility. 
455    *
456    * @param melati - the melati for this request
457    *
458    * @return - "Paid" - a message thanking the user for their order
459    *
460    * @throws InstantiationPropertyException - if we cannot construct trolley
461    */
462 
463   protected String Paid(Melati melati) 
464    throws InstantiationPropertyException {
465     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
466     trolley.confirmPayment(melati);
467     // and get rid of it
468     trolley.remove(melati);
469     return JammyJoesUtil.shoppingTemplate(melati, "Paid");
470   }
471   
472   /** 
473    * abandon a trolley 
474    *
475    * @param melati - the melati for this request
476    *
477    * @return - "Trolley" - the initial trolley page
478    *
479    * @throws InstantiationPropertyException - if we cannot construct trolley
480    */
481   protected String Abandon(Melati melati) 
482    throws InstantiationPropertyException {
483     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
484     // and get rid of it
485     trolley.remove(melati);
486     return JammyJoesUtil.shoppingTemplate(melati, "Trolley");
487   }
488   
489   /** 
490    * force a user to login
491    *
492    * @param melati - the melati for this request
493    */
494   protected void assertLogin(Melati melati) 
495    throws InstantiationPropertyException {
496     ShoppingTrolley trolley = ShoppingTrolley.getInstance(melati,config);
497     // deligate to your trolley
498     trolley.assertLogin(melati);
499   }
500 
501   /** 
502    * create a new item and add it to the ShoppingTrolley
503    *
504    * if a price is passed
505    *
506    * @param trolley - the trolley to add the item to 
507    * @param melati - the melati for this request
508    * @param id - the id of the item to be added
509    *
510    * @return - the new shopping trolley item
511    */
512   private ShoppingTrolleyItem newItem(ShoppingTrolley trolley, Integer id,
513                                       Double price, String description)
514    throws InstantiationPropertyException {
515     return trolley.newItem(id, description, price);
516   }
517 
518   /** 
519    * override the building of the MelatiContext in order to glean the 
520    * additional information required for the Shopping Trolley system
521    *
522    * @param melati - the melati for this request
523    *
524    * @return - the ShoppingContext with as many bits set up as possible
525    *
526    * @throws PathInfoException - if we don't understand the PathInfo
527    */
528   protected PoemContext poemContext(Melati melati)
529       throws PathInfoException {
530     ShoppingContext it = new ShoppingContext();
531     String[] parts = melati.getPathInfoParts();
532     if (parts.length < 2) 
533       throw new PathInfoException(
534           "The servlet expects to see pathinfo in the form " +
535           "/db/method/ or /db/method/troid or /db/method/troid/quantity");
536     it.setLogicalDatabase(parts[0]);
537     it.setMethod(parts[1]);
538     try {
539       if (parts.length > 2 && !parts[2].equals("")) 
540         it.stid = new Integer(parts[2]);
541       if (parts.length > 3 && !parts[3].equals(""))
542         it.quantity = new Integer(parts[3]);
543     } catch (NumberFormatException e) {
544       throw new PathInfoException(
545           "The servlet expects to see pathinfo in the form " +
546           "/db/method/ or /db/troid/method/ or /db/troid/quantity/method/ " +
547           "where the troid is an integer and the quantity is a number");
548     }
549     return it;
550   }
551 
552 }