From: Lincoln H. <dyn...@tf...> - 2004-10-26 03:47:09
|
Thank you all for the prompt response, I have spent days trying to figure this out before I finally decided it's time to ask the experts. My Apache is Apache/2.0.48 on Linux and latest version of webware 0.8.1 The site I am having problem is www.unlockcellphone.com The problem I had was everything was tested by one person at a time, not simultaneously. It is when i had another friend testing it simultaneously I discovered this session problem. Other than this particular problem, the site is working very well. If you can be kind enough to take a look at part of the cart code, I would really appreciate it. I don't have a __init__() function defined in the Cart and if that could be a problem please let me know. Regards, Lincoln ------------------------ from WebKit.Page import Page from WebKit import Session from WebObjects import * import pyPgSQL from pyPgSQL import libpq from pyPgSQL import PgSQL import WebDB from WebDB import * import random import mx.DateTime #required by the pyPgSQL library from submitViaEmail import * import os import md5 #Last modified: Oct 04, 2004 class Cart(Page, BasicItem, ExtendedItem, Shipping): ValidationError = 'ValidationError' connInfo = 'hostaddr=127.0.0.1 port=5432 dbname=moewhouse user=frog password=green' customer = Customer() #store the customer info into the customer object at check out order = Order() #we will need to store the order into the order object at check out itemlist = list() subtotal = 0.00 shipping = Shipping() #Shipping object is part of the WebObjects class total = 0.00 payTax = False # Define the constants CART_ACTION = 1 #used by writeTable() function CHECKOUT = 2 # Define the constants of order status used by orders table. See the most updated values in WebObjects ORDEROK = 0 #order accepted, processing order PHONEWAITING = 1 #waiting for customer's phone to arrive PAYWAITING = 2 #waiting for customer's payment PAYOK = 3 #Payment OK RCVD = 4 #received phone PROC = 5 #processing phone or order SHIPPED = 6 #order shipped BACKORDER = 7 #order is back ordered DELAY = 8 #order is delay due to technical difficulties or payment problems CANCEL = 9 #order has been cancelled REFUND = 10 #order has been refunded RETURN = 11 #order has been marked for return RETURN_RCVD = 12 #returned merchandise received REPLACED = 13 #replacement issued CODE = 14 #unlock code via email UNLOCKED = 15 #Phone has been successfully unlocked if os.name == "nt": #if the OS is Windows NT (2000, XP), the directory is represented by \ self.separator = "\\" elif os.name == "mac": #on a Mac it is : self.separator = ":" elif os.name == "posix": #on UNIX systems it is / self.separator = "/" def calcShipping(self): req = self.request() res = self.response() sess = self.session() shipDetails = {} if not req.hasField('Shipping'): self.write("<B>Please choose a return shipping option</B> <P>") return 0 else: #get the shipping rate from the ship table in the database by matching the shipcode from the form input with the code in the ship table self.shipping.str_shipCode = req.field('Shipping') statement = "SELECT * FROM ship WHERE shipcode = '" + self.shipping.str_shipCode + "';" print "Shipping Statement: " + statement moew_conn = libpq.PQconnectdb(self.connInfo) que = moew_conn.query(statement) print "Result status: " + str(que.resultStatus) + "\n" columns = que.nfields #number of columns in this table cells = que.ntuples #number of tuples/cells for j in range(cells): for k in range(columns): if str(que.fname(k)) == "shipprice": self.shipping.dbl_shipPrice = float(que.getvalue(j,k)) shipDetails[0] = self.shipping.str_shipCode shipDetails[1] = str(self.shipping.dbl_shipPrice) return shipDetails def calcTotal(self): req = self.request() #this assume that the calcSubTotal(), calcTax() functions have already #been called in another function before calling the final calcTotal return self.calcSubTotal() + self.order.tax + self.shipping.dbl_shipPrice def subTotal(self): return self.subtotal #use this method to retrive the subtotal value to avoid unnecessary accumulation #due to the calling of calcSubTotal() def total(self): return self.total #if you use respond() then it will override the actions. #if you don't use respond the the servlet will rely on actions to generate code def actions(self): return Page.actions(self) + ['add', 'remove', 'login', 'submitOrder', 'showOrderDetail', 'accountlogin', 'checkoutReview', 'checkout', 'checkoutNewUser'] def remove(self): try: req = self.request() res = self.response() sess = self.session() item = BasicItem() item.str_itemNum = req.field('Item') isDuplicate = False for item in self.itemlist: if (item.itemNum() == req.field('Item')) & (item.int_quantity > 0): print "\nCurrently removing: " + item.itemNum() item.int_quantity = item.int_quantity - 1 if item.int_quantity == 0: self.itemlist.remove(item) #remove the item entirely from the shopping cart if the quantity is 0 sess.setValue('currentCart', self.itemlist) # self.write('<P>Session value: ', sess.value('currentCart')) # self.write('<P>Session ID: ', sess.identifier()) self.write('<P><i>Removed from cart:</i>', item.itemNum()) self.writeTable(self.itemlist, self.CART_ACTION) #use the pre-defined function to update the cart HTML #write two buttons: the first "Continue Shopping", the second "Checkout" self.write("<FORM action=unlock_index.psp method=post name=frmCart><INPUT TYPE=hidden name=action value='calcTotal'><INPUT TYPE=submit name=button value='Continue shopping'></FORM>") self.write("<FORM action=order_login.psp method=post name=Cart><INPUT TYPE=submit name=Checkout value=Checkout></FORM>") except ValueError, e: self.write(e) def add(self): try: req = self.request() res = self.response() sess = self.session() item = BasicItem() item.dbl_itemPrice = float(req.field('Price')) item.str_itemNum = req.field('Item') if req.hasField('Qty'): #if the form submitted has a quantity field for each item. if Customer().isNumber(req.field('Qty')) == True: #validate it first item.int_quantity = int(req.field('Qty')) else: item.int_quantity = 1 isDuplicate = False for self.i in self.itemlist: if self.i.itemNum() == req.field('Item'): print "Duplicate item found: " + self.i.itemNum() if req.hasField('Qty'): self.i.int_quantity = self.i.int_quantity + item.int_quantity else: self.i.int_quantity = self.i.int_quantity + 1 isDuplicate = True if not isDuplicate: self.itemlist.append(item) #only append a new object if the item does not already exist in the cart sess.setValue('currentCart', self.itemlist) # self.write('<P>Session value: ', sess.value('moew')) self.write('<P>Session ID: ', sess.identifier()) self.write('<P><i>Added to cart:</i>') self.writeTable(self.itemlist, self.CART_ACTION) #use the pre-defined function to update the cart HTML self.write("<FORM action=unlock_index.psp method=post name=frm_Cart><INPUT TYPE=hidden name=action value='calcTotal'><INPUT TYPE=submit name=button value='Continue shopping'></FORM>") self.write("<FORM action=order_login.psp method=post name=Cart><INPUT TYPE=submit name=Checkout value=Checkout></FORM>") except ValueError, e: self.write(e) def writeTable(self, itemlist, formatCode): #the writeTable function takes the current item list and format it according #to the format code. If the code is 1 which is CART_ACTION constant, then it will #add a remove button at the end of each row. If the code is 2, which is CHECKOUT #then it should not show the remove button along each row sess = self.session() self.write('<table style="border: solid 1px;" width=500>') self.write('<tr bgcolor="#AAAABB">') self.write('<td width=150><B>Item</B></td>') self.write('<td width=50><B>Price</B></td>') self.write('<td width=50><B>Qty</B></td> <td><B>Sub-Total</B></td> <td></td></tr>') for item in itemlist: self.write('<TR><TD>', item.itemNum(), '</TD>') self.write('<TD>$', item.dbl_itemPrice, '<TD>', item.int_quantity, '</TD><TD>', item.getSubTotal(), '</TD>') if formatCode == self.CART_ACTION: self.write("<TD><FORM action=Cart method=post name=cart><INPUT TYPE=submit name=_action_remove value='Remove'><INPUT TYPE=hidden name=Item value='", item.itemNum(), "'></FORM></TD></TR>") self.calcSubTotal() if self.subTotal() == 0: self.write('<TR><TD colspan=3></TD><TD><B> No item in your cart </B></TD></TR>') else: self.write('<TR><TD colspan=3></TD><TD>Sub-Total: $', self.subTotal(), '</TD></TR>') self.write('</TABLE>') sess.setValue("orderObject", self.order) def writeCustomerInfo(self): #format the customer info we stored in the session for checkout review purpose res = self.response() sess = self.session() try: cust = sess.value("customerObject") self.write("<B>Customer ID: </B>", cust.str_customer_id, "<P>") self.write("<B>Shipping Address:</B><P>") self.write(cust.str_firstname, " ", cust.str_lastname, "<BR>") self.write(cust.str_addr1, "<BR>") self.write(cust.str_addr2, "<BR>") self.write(cust.str_city, " ", cust.str_state, " ", cust.str_zip, "<BR>") self.write(cust.str_country, "<BR>") self.write("(", cust.str_phone[:3], ")", cust.str_phone[3:6], "-", cust.str_phone[6:], "<BR>") self.write(cust.str_email, "<BR>") #self.write(cust.str_default_payment) self.write("<FORM method=post action=edit_customer.psp name='ModifyCustomer'>") self.write("<INPUT TYPE=submit name=_action_modifyCustomer value='Modify'></FORM>") except: res.sendRedirect("unlock_index.psp") print "No customerObject in session found! Redirect to shopping page" return 1 |
From: Lincoln H. <mp3...@tf...> - 2004-11-03 08:01:10
|
Thanks for all who replied with good answers. I am working on fixing this problem. Now I am curious about the technical design of the servlet. I wrongfully assumed that a new servlet is loaded per connection, now I understood it is not the case. Because the servlet is re-used, that's why whatever I declare as part of the class of the servlet is shared by different connections. Since the servlet is loaded once and shared among all connections, what are the exact parts that explicitly tells the servlet to keep the data unique to the connection - I assume this would be the session right? Is it better to put the non-shared data into __init__ (which is still part of the servlet class) or awake() ? Lincoln |
From: Sam N. <sa...@se...> - 2004-11-03 09:23:53
|
Lincoln H. wrote: > Thanks for all who replied with good answers. I am working on fixing > this problem. > Now I am curious about the technical design of the servlet. I wrongfully > assumed > that a new servlet is loaded per connection, now I understood it is not > the case. > Because the servlet is re-used, that's why whatever I declare as part of > the class of the > servlet is shared by different connections. Since the servlet is loaded > once and shared > among all connections, what are the exact parts that explicitly tells > the servlet to keep > the data unique to the connection - I assume this would be the session > right? Is it better > to put the non-shared data into __init__ (which is still part of the > servlet class) or awake() ? > > Lincoln Hi Lincoln, __init__() is called at webware startup (when it first loads the servlet). whatever you put in here persists for the duration of the servlet's lifetime. the servlet's lifetime could be equal to the webware server's lifetime (weeks, months, years). awake() is called when the servlet is woken up for a new request. any request specific initialization should be done here. this will of course persist along with the servlet just like __init__, so... sleep() is called when the servlet is finished handling a request. you need to make sure to clean up any variables etc. that were set during awake or during the process of the request. do whatever you need to do to clean up for the next request. Recap: __init__ - set up servlet on app server startup - runs only once awake() - set up request - runs once per request sleep() - clean up request - runs once per request to put this all together, here is a simple example from one of my SitePage classfiles: def awake(self, transaction): Page.awake(self, transaction) self.script_uri = self.request().serverDictionary()['SCRIPT_URI'] self.success = '' self.error = '' self.message = '' def sleep(self, transaction): self.mysql.Disconnect() Page.sleep(self, transaction) and here is the source code from the webkit Page classfile: def awake(self, transaction): HTTPServlet.awake(self, transaction) self._response = transaction.response() self._request = transaction.request() self._session = None # don't create unless needed assert self._transaction is not None assert self._response is not None assert self._request is not None As you can see, it is important to call the parent's awake method - Page.awake() - if you override awake. Same goes for sleep. Hope this helps you to get an idea about the design/flow of webkit. I find that a little time spent browsing the webkit source code (especially Page, Session, and other well used classes) really helps my understanding of how the framework works. Considering that the python code is well written and fairly simple, and that to use webkit we actually *extend* it, the source code presents itself as the best documentation for this project. - Sam Nilsson |
From: Stephan D. <ste...@gm...> - 2004-10-27 14:18:00
|
On Tuesday 26 October 2004 05:46, Lincoln Han wrote: > Thank you all for the prompt response, I have spent days trying to > figure this out before I finally decided it's time to ask the experts. > > My Apache is Apache/2.0.48 on Linux and latest version of webware 0.8.1 > > The site I am having problem is www.unlockcellphone.com > > The problem I had was everything was tested by one person at a time, > not simultaneously. It is when i had another friend testing it > simultaneously I discovered this session problem. Other than this > particular problem, the site is working very well. > > If you can be kind enough to take a look at part of the cart > code, I would really appreciate it. > > > I don't have a __init__() function defined in the Cart and if that could be > a problem > please let me know. > > Regards, > > Lincoln > > In addition what Geoff just wrote: you have for example the line sess.setValue('currentCart', self.itemlist) which sets a link to on instance variable to the session object. The 'self.itemlist' might be end up in some other 'Page' as well. Instance attributes might outlive a specific page as they are recycled. Hope that helps Stephan |
From: Max I. <ma...@uc...> - 2004-10-27 15:09:05
Attachments:
store.py
|
Lincoln Han wrote: > > class Cart(Page, BasicItem, ExtendedItem, Shipping): > > > customer = Customer() #store the customer info into the > customer object at check out > order = Order() #we will need to store the order > into the order object at check out > itemlist = list() > subtotal = 0.00 > shipping = Shipping() #Shipping object is part of the > WebObjects class I didn't read furher, but the above is a prominent sign IMO. Your order, customer and other objects are class variables! That means they would be shared for all Cart servlet instances. You must not put an "order" even to a servlet variable, not to mention class var but store them in Session object. For those that can't be stored there (as they, say, contains a db connection or simply not pickable) you could use, say, a global dict (singleton) Here is a version I wrote to address this issue (attached). |
From: Tracy S. R. <tr...@ax...> - 2004-10-27 15:20:46
|
It looks like the customer and it's related items are being set as *class* variables instead of *instance* attributes. Class variables will be shared across all instances. All of the code in the various page methods are manipulating the same variable in the class. You're going to have to extract the customer and itemlist instantiations into some other place. Maybe in the page's awake method you could check the user's session object to see if they have a customer and itemlist object set already, if not, create them and put them into the session object: def awake(self, transaction): Page.awake(self, transaction) if not self.session().hasValue('customer'): self.session().setValue('customer', Customer()) if not self.session().hasValue('order'): self.session().setValue('order', Order()) etc... Then, in all your logic/manipulation code, first get the object you want to manipulate and then do what you need, i.e.: def test(self): order = self.session().value('order') order.addSomething(...) But, you're going to have to put *everything* that is customer-specific into a session variable. It might be worth it to abstract out a larger container class that has an API into all of the sub-components... --T On Oct 25, 2004, at 10:46 PM, Lincoln Han wrote: > class Cart(Page, BasicItem, ExtendedItem, Shipping): > > ValidationError = 'ValidationError' > connInfo = 'hostaddr=127.0.0.1 port=5432 dbname=moewhouse > user=frog password=green' > > customer = Customer() #store the customer info into the > customer object at check out > order = Order() #we will need to store the order > into the order object at check out > itemlist = list() > subtotal = 0.00 > shipping = Shipping() #Shipping object is part of the > WebObjects class > total = 0.00 > payTax = False |