dcsimg
Linux Today: Linux News On Internet Time.




More on LinuxToday


JavaBoutique: Open Source Shopping Cart [Part 2]

Jul 02, 2000, 13:46 (0 Talkback[s])
(Other stories by Lane Friesen)

WEBINAR:
On-Demand

Re-Imagining Linux Platforms to Meet the Needs of Cloud Service Providers


by Lane Friesen, JavaBoutique

Illustration: Example 3: A Pseudo-Constructor Example

Notice, in this code, that neither applet "app" nor Frame "ShopFrame" contain any variables at all. All variables are contained in the class ShopPanel. The applet, in its method init(), refers to ShopPanel, which in and of itself forces all of ShopPanel's declared variables, all of which here are static, to be instantiated. In particular, shopFrame and counter are formed. The applet's life cycle then calls init() in the applet, which transfers the call to init() in ShopPanel. ShopPanel's init() calls formPanelElements() in ShopPanel, which sets the characteristics of the already instantiated objects in ShopPanel. The next call is to shopFrame, which now exists, and has defined characteristics, and in particular to its method formPanel(), which takes the frame elements defined in ShopPanel, and sets them into shopFrame, which is also defined in ShopPanel. Event handling can now be set up, in shopFrame's formPanel(). Events are caught in shopFrame, but the event handlers are all located as static methods in ShopPanel. Notice that this structure is not dependent upon the applet at all: there is a call from "app" to ShopPanel, but nothing from ShopPanel back to applet "app." The frame shopFrame, which is created indirectly by the applet through its reference to ShopPanel, in fact knows absolutely nothing about its parent-- it is aware only of the class ShopPanel, which is not being instantiated. When page transfers are made, it is the applet which is altered from active to inactive. Notice how short it is, and how little time will be required to reload it. The frame doesn't change at all; it simply sits there, and watches as you browse.

Create the following applet:

import java.applet.*;

public class app extends Applet{
        public void init(){
                ShopPanel.init();
        }
}

import java.awt.*;
import java.awt.event.*;

public class ShopFrame extends Frame implements ActionListener{
        void formPanel()        {
                add(ShopPanel.counter);
                ShopPanel.counter.addActionListener(this);
        }

        public void actionPerformed(ActionEvent e){
                ShopPanel.increment();
        }
}

import java.awt.*;

//notice that it can be public class ShopPanel, not just abstract
        class ShopPanel. We will discuss this.
public class ShopPanel{
        static ShopFrame shopFrame = new ShopFrame();
        static Button counter = new Button("Click");
        static int r = 0;

        static void formPanelElements(){
                shopFrame.setBounds(200,200,100,100);
        }

        static void init(){
                formPanelElements();
                shopFrame.formPanel();
                shopFrame.show();
        }

        static void increment(){
                counter.setLabel(new Integer(r).toString());
                r++;
        }
}

Place it into the following HTML page:

<HTML>
<HEAD>
</HEAD>
<BODY>
        <applet code=app.class name=app width=1      height=1  VIEWASTEXT></applet>
</BODY>
</HTML>

Let's look now at what our example does. The applet indirectly creates a frame. Clicking this frame increments a number (There may be problems in some browsers-- we are not including minor tweaking elements, composed solely of standard JAVA, that solve these "glitches."). Moving away from the HTML page does not destroy the said frame. Rather, it continues to operate as a kind of "terminate and stay resident" web program. Notice that the said frame now maintains its position from page to page automatically, without any user intervention, unlike the previous two examples. This is because it is independent of what happens to the pages. When created, it is tied to the JAVA virtual machine through the static variables, and is then never destroyed. This demonstrates that we have moved a stage further in establishing persistence. Since the frame is only created once, there are none of the stability problems that were previously associated with the Abstract Windows Toolkit. Transition between pages is essentially instantaneous; whenever access to the underlying HTML is desired, then the very small source applet is simply reloaded on that page. It provides the link to ShopPanel, from the underlying HTML.

Incidentally, if you look closely, you will notice that ShopPanel this time is not an abstract class. A pseudo-constructor for an abstract class must follow a very specific initialization procedure in order to work. It turns out that if the same sequence is followed within a normal class, using the same static variables and static methods. This will then also lead to an object that "lives forever." However, a normal class can contain a constructor, and thus it can do more things. We have come further.

The two methods for creating persistence (reloading and pseudo-constructors) cooperate beautifully. Our shopping cart, for instance, is created once, and then never destroyed. Applet reload gives it continuing access to the underlying HTML, through Javascript and LiveConnect, and through the applet lifecycle, which can pass current context information to the persistent class. Creation of the shopping cart can occur on any of the pages that are being reloaded-- one simply sets a boolean variable "firstTime" to true in the initialization statements in ShopPanel, and when the frame is created, one resets it to false. The change in the variable is remembered at a reload; the frame is created only when "firstTime" is true.

Open Source Shopping Cart Code

The following two lines of code are inserted into any HTML page that wishes to offer items for sale; I usually place them right after the <BODY> tag:

As mentioned earlier, one must add a CODEBASE=../, or some equivalent, to the <APPLET> tag when the HTML file is in a directory other than the one that contains the applet. A look at the source code posted elsewhere will give examples.

        <APPLET code=app.class height=1
                id=app name=app width=1 archive="app.zip"></APPLET>
        <SCRIPT LANGUAGE=javascript>function buy(a,b,
                c,d){document.app.prod(a,b,c,d)}</SCRIPT>

These lines load an applet called app.class. The code for "app" is as follows:

import java.applet.*;
public class app extends Applet
{
        public void start()
        {
                ShopPanel.initValues(this);
        }
        public void prod(String iD, String it, String pr, String sh)
        {
                ShopPanel.prod(iD,it,pr,sh);
        }
}

Let me make some comments for Open Source developers. First, the applet is the segment that is being reloaded, and it is short. This makes page to page transfer essentially instantaneous.

Second, HTML memory depends upon the applet being restarted, not reloaded. This appears to happen invariably, with an applet this size, in all current browsers. However, as features are added to the applet, one can begin to trigger a reload rather than a restart, particularly in HTML pages that are very complex. Pseudo-constructors continue to do their job; the result is a frame which persists, and whose purchases can still be finalized, but which is no longer accessible by any HTML page. It has become "detached." Further purchases bring up a second frame, which also persists. All further purchases are now added to the second frame. In order to keep size down, and to ensure restarting rather than reloading, Open Source programmers will probably find, as I have, that it is best to make changes in classes other than "app."

Third, notice that we call start() in the applet, and not init(). This is because init() is called the first time that an applet is loaded, whereas start() is called every time that a page transfer is made. Our goal is to update the rest of the code with the current address of the restarted applet every time the applet is reloaded. By passing on the value of "this", we can then use commands like "this.showDocument()." This continual updating can only be done by calling start().

Extended Examples

As in the simple pseudo-constructor example given previously, class ShopPanel uses an extension of the Frame class called ShopFrame. The code for ShopFrame is as follows:

Click here to view the first .java code example

Notice that no variables are declared in ShopFrame. It does not contain a constructor. It will be instantiated by means of a pseudo-constructor, contained in ShopPanel.

The core of the Open Source shopping cart program is contained in class ShopPanel. This is probably where most future program modifications would occur.

Click here to view the second .java code example

As mentioned in the pseudo-constructor example, the shopping cart works just as well if a normal, rather than an abstract class, is used. I keep the class abstract because I am then warned, by the fact that the program won't compile, whenever I step outside of the proper rules for generating persistence by means of a pseudo-constructor.

You will notice that some graphics commands are repeated twice. This is because either Netscape or Explorer is not responding correctly to the first command. Probably, there is a timing problem with the peer. Repeating the command at a later point appears to solve the problem. Open Source developers may wish to experiment extensively if they choose to change the position of these repetitions.

Notice that all variables are declared here in ShopPanel, and that many of them are static. Single-stepping through program execution will demonstrate clearly how a simple reference to ShopPanel in the applet "app" causes all static variables in ShopPanel to be instantiated before ShopPanel is ever given control of program execution; everything is always defined, and it is done in the proper order.

A call to "parent.isActive()" is used to determine whether the applet, in its updated form, has been reloaded by the current HTML page. This check could be ommitted, but a security exception would then be thrown by any call to showDocument() that occurred when off-site, and this would need to be handled by the browser. I felt it was better to do things explicitly.

As you notice from the code, the program attempts to load a file called "update.txt" from the directory in which the applet "app" is located. Here's a sample "update.txt":

"1",$14.97
"2",$22.93
"3",$6.22
"4",$5.55
"5",$6.66
"6",$7.77

"update.txt" is a text file written in a format consistent with a two-field Access database, in text format. It allows a merchant to alter prices temporarily, should he want to offer a sale. This file can be multiple megabytes in size. It is cached by the applet. Page to page transfer in HTML is always instantaneous.

The Shopping Cart and Database

A non-commercial site is altered into a commercial site through the inclusion of two lines of "boilerplate code" into the HTML of all pages that wish to present items for sale. A specific item is then offered for sale by including an

onClick="buy('itemID','itemName','item
        Price','itemShippingProcedures')"

event handler into any page object which is able to support it: this may be a button, a diagram, some region of an image, or even a JavaScript program. When the client operator triggers an event in this object on his browser, a call is made by the event handler to the applet or object which was loaded into the page, and the applet or object adds the item to a shopping cart frame or form which this applet or object brings up whenever a sale is made. None of this involves any interaction whatsoever with the host server.

When the "buy" event is triggered by the browser operator, the applet or object, in our particular implementation, checks to see whether an item with "itemID" is located in a cached database on the browser. If it is, then it assigns the associated cached price to the object, and displays this in the shopping cart frame or form. If it is not, then it uses the number that is hardcoded into the HTML as "itemPrice." Again, this involves no interaction with the host server. The fact that everything occurs on the client, and can use the full power of the client's processor, means that database access involves no noticeable delay, even when the database is quite large.

"itemID" is the key and must always be present. "itemPrice" may be an empty string. When it is, then the database must always be present, and it must contain "itemID" and a corresponding "itemPrice."

The database in our implementation is designed with Microsoft Access, and can thus be integrated into any large-scale database. It is a two column table in text format, and can thus also be designed by a novice operator using a simple text editor. To be recognized by the said applet or object, the database in our implementation must be stored on the merchant's server in the same directory as the applet or object, and it must be called "update.txt." When the applet or object is first loaded by some particular page, it checks for the presence of this file in its codebase directory. If the file is found, then the applet or object loads it, and it then generates a cache, on the client, for subsequent pages. In our implementation, this cache is a static String in an abstract JAVA class, and it persists. The string may be well in excess of 1 Meg in length, and could thus include more than 40,000 revisions. If the database file is absent, the applet or object remembers this as well-- in our case as an altered boolean variable in the JAVA code. No further server-client interaction is attempted, or necessary.

Once "itemPrice" has been checked with the (optional) database, which is cached on the client, then the applet or object, which also resides on the client, places the result in a frame or form on the desktop window. Sales information, as it accumulates, is stored as a static Vector in the JAVA code, and it persists. The result of the purchase can therefore be collated with prior purchases, and a running total developed and displayed.

Since cumulative sales information has persistence, it is possible to alter previous purchases without going back to the pages on which they were presented, and without interacting in any way with the host server. "UP" and "DOWN" buttons allow scrolling through the list of purchases, and "MORE" and "LESS" buttons allow prior purchasing decisions to be altered.

If "CLEARCART" is pressed, then the frame is made invisible. The "Window Close" button on the frame is not enabled because, as Open Source developers will discover, its use causes undesirable side effects.

Checkout

When the client operator is ready to finalize his purchases, then, in our implementation, he presses "CHECKOUT..." This sends the purchase information, as name-value pairs appended to a hard-coded HTTP address, to a central server that is equipped to ask for name, address, and credit-card information. The codebase of the applet or object is also included; this is used to check that the merchant's site has registered with the central server, and that it is authorized to do business.

When the said "CHECKOUT.." button is pressed, the cart is automatically cleared. Pressing "RESET", however, brings the information back, in case the user wishes to change his mind.

In our implementation, one of the fields in the "onClick=buy(a,b,c,d)" event handler is a string that encodes every possible variation of tax and shipping instructions. This information is passed, when a purchase is made, to the said applet or object, which in turn passes it through unaltered to a central server, when the order is finalized, and the said central server processes the order accordingly. I would imagine that there will need to be a table of hundreds of strings; the merchant, in programming the HTML, would flip through the choices and choose the right one, for each item. For instance, one string might means: "Charge $10 shipping and send by UPS." It seems logical that each particular merchant's location-- country, and then state or province-- would be recorded initially when he signed up with the order-processing company. That would be one less thing to encode. If it was done that way, then access to a database at the central computer could work out the proper taxes. The important point is that people at the central location would not ever need to talk to the various multiple merchants. It would then be cost effective for them to provide quality services for a low price.

This shopping cart front end is Open Source. Any commercial company that wishes to provide back end checkout services is free to adapt it to its use, in whatever manner it wishes. However, the techniques of applet reloading and pseudo-constructors used to generate persistence are covered by patent. I give unlimited permission for anyone to use the patents for a shopping cart, and an associated database cache. Any extension of these methods beyond a shopping cart and an associated database cache not only destroys bandwidth for the shopping cart, but is also a violation of my patent rights.

If some company requires a more formal arrangement, before committing resources, I can sign a legal form guaranteeing non-exclusive permission to exploit the patents, as the company wishes, for a shopping cart, and an associated database cache.

In closing, let me suggest one alteration that allows a company to handle multiple merchants on the same cart, at the same time that it also handles orders from the previous small business cart.

Cart for Multiple Merchants

When I demonstrated the small business version of the shopping cart to a firm in Seattle, they asked whether it would be possible to place multiple merchants, all hosted on the same site, onto one frame, so that all purchases could be updated at any time in the browsing experience, and finalized simultaneously. You can see the result at http://209.87.142.42/is2.

To handle multiple merchants, I rewrote class ShopPanel as follows:

Click here for the third .java code example

The boilerplate code for each HTML page was altered in the following way:

<APPLET code=app.class height=1 id=app name=app width=1 archive="app.zip" VIEWASTEXT>
        <param name=start value="25">
        <param name=length value="3">
</APPLET>
<SCRIPT LANGUAGE=javascript>function buy(a,b,c,d){document.app.prod(a,b,c,d)}</SCRIPT>

The <APPLET> tag of course would also need an appropriate CODEBASE="whatever" addition, as described previously, and demonstrated in the source code at http://209.87.142.42/is1.

The big change in ShopPanel was the addition of two applet parameters and their associated code, and the removal of the optional database. This resulted in code that was now only 13K in length.

Conclusion

The coding challenge was to find some way to distinguish the companies, all of which now resided on the same server, and thus had the same applet codeBase. I chose to have the applet read the documentBase for the particular HTML page, and to snip out the portion which represented the merchant's particular root directory. No matter what sub-directory the browser was in, the portion at the beginning of the http:// address, up to and including the root for that merchant, would always be the same. The first applet parameter, named "start," told the applet where to begin snipping. The second, named "value," told it where to stop. For instance, if the merchant address was http://www.infinitestores.com/oneMerchant, "start" in the zero-based count would be 29, and length would be 11. This would snip out the string "oneMerchant," and ShopPanel could then place it in the slot reserved in the small business version for shippingAndHandling.

Every merchant would call the same applet, preferably using a CODEBASE=http://www.absoluteAddressOnServer call (for an example, view the source at http://209.87.142.42/is1). The fact that the absolute call was for an applet on the same server as the document would ensure HTML memory. However, an absolute call would mean that there would be no need to keep track of directories and sub-directories. This could be important on a large site with multiple servers. Every time the applet was reactivated on some page, it would reload the parameters from that particular HTML page, and if these were different from those in some other reactivation on another page, the values would be modified. Merchants could thus be distinguished.

The company director told me that, in his experience, many orders were canceled at checkout when buyers realized the full impact of shipping and handling. He wondered if it was possible to place a price in the cart that reflected the full cost of shipping and handling. I suggested that he include a call in ShopPanel to the appropriate database on his site, and update the price as desired. There would be an initial result, from numbers placed into the HTML (probably by his databases, perhaps using active server pages), and then a few moments later an update (taking account of volume discounts based on amount purchased, if applicable, and so on). Since JAVA is multi-threaded, and separate from HTML, this could take place simultaneously with other purchases, and independently of HTML page transfers. Since pseudo-constructors maintain the JAVA frame and code when off-site, it would not matter if the buyer jumped elsewhere in the middle of an update. With code size reduced to 13K, there was plenty of room.

Using this modified cart, the company could now handle both a multi-merchant cart, and small business carts. If the applet codebase in the checkout querystring was the company's site, then the company would know that the shippingAndHandling slot for each product provided the respective merchant's unique directory name, and interact with databases appropriately in order to finalize the order further. Alternatively, if the applet codebase was something different, then that codebase would be the merchant's identity - it would be unique because every http:// address is different. All aspects of a small business transaction would be automated - from initial sign-up, to database maintenance through an optional text file on the merchant's server, to shipping and handling calculations (according to coded instructions in the shippingAndHandling string for each product). Since there would be no need for personal communication, it would make economic sense to deal with the small merchant.

Everything would be interrupt driven: from JavaScript event handlers inserted into elements on an HTML page calling the applet to indicate a purchase; by the applet calling an appropriate database for information; or by the user finalizing an order and through this, sending a querystring to an order-handling section. Persistence would be maintained on the browser, freeing up central server resources, and ensuring a fast connection. There would be smooth interaction between merchants hosting on the same site, and those hosting on other servers.

The multi-merchant version of the cart at present retains the shippingAndHandling field in the HTML coding, even though this information is no longer needed by the multi-merchant site - shipping and handling would be handled through interaction with various databases. The fact that the HTML is unchanged allows a particular merchant to move back and forth easily from hosting at the Seattle company-- with the advantage of placing his orders on a single shopping cart with those of others-- to hosting elsewhere for less money, with the penalty of having his own cart. He does not have to make HTML modifications to his site. Class ShopPanel in the multi-merchant version in turn overrides any ShippingAndHandling information that it receives and instead passes on the merchant's directory string. The fact that the querystring format is unchanged in the multi-merchant version presumably should make things easier at the server end.

I look forward to seeing modifications that will be made by other Open Source developers, and implementations by various companies. I trust that competition will bring us quality service for a low price.

Related Stories: