ELJonline: Brewing Java at the Point of Sale
Nov 1, 2001 — by LinuxDevices Staff — from the LinuxDevices Archive — viewsRetail operations can benefit from Java features, especially dynamic class loading.
Many of us don't think about it, but the point of sale (POS) is a remotely managed application and has been for years. Next time you visit a restaurant chain or an enterprise grocer, think about the technology behind the cash register and what must be involved in updating prices and software. In practice, the state of these enterprise systems ranges from off-the-shelf, standalone registers backed by an army of key-entry personnel to highly customized (proprietary) systems using various high-speed technologies to form vast private, wide-area networks. Few standards exist, and the implementation of those standards is ad hoc at best. One common theme is all of the software is proprietary and expensive, putting enterprise functionality out of reach for smaller operations and single shop owners.
This article examines an open-source POS application written in Java and its use of certain Java functionalities to reduce complexity in order to implement a broadly applicable POS transaction engine. To many Java-literate readers, the features described may be old news, but it may be of interest to see some of the more arcane Java features used in a non-web application. Also, since this article describes an open-source project for enterprise retailers, it goes into considerable architectural detail regarding POS functionality as it applies to enterprise retail environments.
Why Java?
Java has emerged in recent years as a common platform for retailers to use in order to reduce software complexity and management requirements. Specifically, Java supplies many of the tools required to build applications with a smaller footprint (examine the Java SDK at 50MB compared to the Windows SDK footprint). Java includes unique functionality not widely available in linked architectures, such as dynamic class loading and serialization. As a footnote, it is possible to perform dynamic class loading in C++ under Linux, but it is not widely used and is not regarded as a fundamental language feature as it is in Java.
The resounding argument against the use of Java is its large memory footprint and slow performance. In practice, however, this argument is losing its punch. Today, it is no longer possible to buy a new computer too slow to use Java, and smaller devices are beginning to be well-supported by the Java ME (micro edition) platform. To be honest, I can't say that Java is poised to take the retail world overwhelmingly by storm. Many retailers already have a large installed base of older hardware that they are not ready to throw away. Furthermore, universal Java support for retail peripherals by the major hardware vendors does not currently exist. The industry is changing, though, and quickly. Most hardware vendors have recognized the advantages of the Java platform.
POS OO Architecture
POS architecture provides a classic exercise in object orientation. First, you have something called an electronic journal, which is basically the memory version of your receipt. The journal contains lines for the various subcomponents of a sale, such as an item, a tax record or a tender (cash, check) record. They all have an amount, a quantity, etc. Are you starting to visualize the abstract interface? You also have devices, receipt printers, customer displays, scanners and scales. Look at these statements if you still need convincing:
receipt().print (ejItem);
receipt().print (ejTax);
and
operatorPrompt().print (ejItem);
operatorPrompt().print (ejTax);
It's quite easy to design an object-oriented POS application, and you could do this in any OO language. Java, however, has some language features that are attractive in an enterprise retail environment. These features generally can be categorized in the context of remote support and building generalized applications. But first, let's look at some additional design criteria intended to justify the use of Java's other features.
Event Engine
If you want a POS that can be applied across retail disciplines (grocery, hard goods, restaurant) and across international borders, then what you want to create is an event engine. This is an application capable of processing generic events and realizing the results of these events in the context of arbitrary hardware devices. The event concept is pretty easy; many modern development tools revolve around the event model, and Java is no different. What is important about a generalized POS application is the events need to be user-programmable and preferably not statically linked. You also need a way of stacking them to create dialogs.
Dynamic Class Loading
We still haven't examined anything that could not be implemented in any OO language, so let's do that now. This POS application makes use of dynamic class loading as part of its central event-engine logic. Take a look at the following SQL POS key definition:
create table pos_key (
key_id integer,
key_type integer,
key_val integer,
key_code integer,
x_loc integer,
y_loc integer,
key_class varchar(100));
The Java class invoked when the key is pressed (key_class) was loaded dynamically into a PosEvent object when the application initialized (actually when the operator logged on). PosEvent is an abstract class that encapsulates discrete POS business logic. When a key is pressed, the following code is invoked: posEvent().engage();. The engage() method contains the business logic for the event, and posEvent() is the key that was pressed. Events in a POS are things like pressing the total key or scanning an item. But there are also dialogs such as a check validation, credit-card validation or an operator login. This is implemented by stacking events and maintaining state through those events.
A Closer Look at Dynamic Class Loading
In its simplest form, dynamic class loading looks something like this:
Class class = findSystemClass(className);
PosEvent posEvent = (PosEvent) class.newInstance();
where className is the string representation of your class, com.globalretailtech.pos.events.TotalKey, that extends PosEvent. The class loader in this application extends java.lang.ClassLoader (findSystemClass()) and uses the newInstance() method from Class to instantiate an object. You also need to wrap it in exception handlers to catch class not found and class cast exceptions.
Beyond Events, Promotions
Retail computing environments change daily. In the CRM (customer relationship management) world we call them the three Ps: products, pricing and promotions. It's the same thing in the retail world; new products are added and removed, prices change daily, and new, better (subtler?) promotions are always being invented by crafty sales types. It's this third category that makes dynamic loading especially useful in retail environments.
A promotion may be a simple percentage off an item, or it may be a more complex operation. For example, if a customer buys three or more bakery products, the cashier will be prompted to upsell the margarine that was double-ordered last month. They both work the same; a promotion is applied to one or more items within the sale.
So what does dynamic loading do for promotions? It would be ugly, if not difficult, to capture both of the above logical operations in a single promotion module and then use some parameter to activate them, which is typically the case for many POS applications used today. A promotion module captures many of the various common promotions that enterprises like to use. Then the software vendor adds whatever custom promotions are requested by the retailer, and they are all managed through heavy parameterization. Some vendors have scripting capabilities, but this adds the overhead of the interpreter. In the end you have a promotion module with a lot of stuff you don't use and a bunch of parameters to manage.
On the other hand, if you load a promotion dynamically, linked to an item or product group, it is small, easy to manage and you can pick and choose what promotions you like to use. Furthermore, if a promotion is linked to an item or product group (via the database), you have a consistent mechanism for managing this functionality in the field. A promotion module doesn't have to be used only for promotions; it could also be used to collect demographic information related to an item or a group of items.
Remote Support, Smaller Deliverables
The Java POS application uses dynamic loading in many places. As previously stated, all button/key logic and graphical components are loaded at login. There are also a number of interfaces used to manage enterprise localization: a math interface so an enterprise can add, subtract and round in their own way; a check digit interface for validating credit-card numbers; EAN/UPC (bar codes); and, as mentioned before, promotion and item-modifier interfaces. I view interfaces as another way of saying, “I don't want to get involved in this argument, just let me know when it's over.” All of the implementing classes are either loaded at login or when the associated class or data record is referenced.
Since dynamic loading has no perceivable overhead, a class has to be loaded to be used. A class is not loaded until it's referenced, so if you've been writing Java, you've been using dynamic loading already.
Dynamic loading allows us to architect a flexible application, but there is also good business sense behind it. Since events are invoked through interfaces, the actual class can be changed underneath the application without relinking. More precisely, and more advantageous to retailers, new logic can be inserted into remote locations in a very granular manner. This means not having to deliver a 5MB executable or DLL simply to add a single feature or fix a bug. Instead, a 5k class file can be delivered. In this application a 2,500-byte Java source file compiles to a 1,800-byte class file. Look at some of these file sizes and the granularity achieved using dynamic loading:
- CashTender.class: 1,203
- CashTender.java: 1,997
- CheckTender.class: 1,206
- CheckTender.java: 2,145
- ClearKey.class: 2,917
- ClearKey.java: 4,300
- CloseCashDrawer.class: 1,896
- CloseCashDrawer.java: 2,338
- CreditTender.class: 1,209
- CreditTender.java: 2,150
- Discount.class: 2,196
- Discount.java: 2,867
Every retailer is required to update their store operation several times during the year and many more if they frequently change the business logic in the store. Since most of this updating is performed through dial-up lines, the size of updates is a serious consideration. As a result, scheduled software updates are limited to just a few per year, usually three to four. Of course, there are the unscheduled ones as well. But, we are doing more here than giving the retailer a way to reduce their software update task–we are giving them more functionality. Promotions, therefore, can be delivered on an ad hoc basis since the promotion class is loaded when the item is looked up. This architecture and granularity also reduces regression-testing requirements.
Internationalization
Another Java language feature is the Locale class found in java.util. This class, in combination with the Number and DecimalFormat class, makes formatting monetary values in different currencies quite easy. A frequent requirement in a POS application is currency conversion, the ability to take a payment in one currency and give change in another. The following code sample formats a double value in the requested currency format:
public static String toMoney (double value, java.util.Locale locale) {
if (locale == null) locale = application.locale();
Java.text.DecimalFormat decimalFormat =
(Java.text.DecimalFormat)
Java.text.NumberFormat.getCurrencyInstance(locale);
return (decimalFormat.format(value));
}
If the locale is not provided, an application default is used, and application.locale() returns a static instance of the locale. The NumberFormat factory returns a format object capable of generating the local currency format, including the currency symbol (if your character set supports it) and even the local representation of a negative value (a credit).
Further internationalization is enabled through the unicode character set supported in the String class and by maintaining all literal strings in the database. Of course, the database also supports Unicode.
Devices
What about POS peripherals? Output devices have been captured in two classes. One handles spooled devices that generally perform output, the receipt class. The second class interacts with the user, the prompt class. Neither of these have any special properties enabled by Java, except for the fact that they, and the devices they control, are loaded dynamically. They simply provide an API for lower-level devices. Some of their methods were used in the initial code sample.
Hardware devices that generate solicited and unsolicited input are handled as part of the event engine enabled through the JavaPOS DataEvent and DirectIOEvent classes. (JavaPOS is a Java retail peripheral interface standard.) For example, a credit-card tender would require an operator to enter the credit-card number and expiration date. This dialog manifests itself as a series of POS events. If a card reader is present, it completes the required events for the operator.
The application also includes a menu interface designed for use in fixed-sized, touchscreen environments. Panels of menu buttons can be defined and attached to POS events or defined as navigation buttons for moving about within the layered panels. Display panel pseudo-devices provide operator feedback in the form of prompts and scrolling operator receipts, and they are updated using the same mechanism as hardware peripherals. These devices can be of arbitrary complexity (that is, they can implement their own dialog logic) and communicate with the application by implementing the PosEvent interface.
Java Mobile Agents
This is my favorite use of dynamic loading. A Java class may be serialized, that is converted (state and logic) to a serial byte stream. The most common use of this functionality is in the storage of persistent objects in a database or file.
This application makes use of mobile Java agents to facilitate remote management functionality. Take a look at the Agent interface:
public interface Agent {
public void init() throws AgentException;
public void onArrival() throws AgentException;
public void migrate (String host, int port) throws AgentException;
}
Agents travel from host to host, or application to application within a host, do their duties and either exit or travel to another location. They may accumulate data as they travel and return to their origin, or perform a task at each host and exit. It's all in how you program them. Agents provide a foundation for an extremely flexible remote management system. I have developed agents that search other POS nodes in order to retrieve sales information, update data and duplicate sales information on other systems. Since no logic resides at the target system, the possibilities are endless.
An agent is supported by a server process running on the target system. The server listens on a well-known IP port, receives the serialized agent, instantiates the agent, then calls the onArrival() method; the rest is up to the agent. If the agent has another host to visit, it calls migrate() itself. To start the whole thing off, a standalone process or user interface loads an agent (dynamically), then calls init() for it. This allows it to do any local initialization before migrating, then migrate() is called.
The server in the POS application runs as a thread. This allows direct interaction with the application, simplifying much of the synchronization required in data updates. For example, an agent delivers price updates. Once instantiated within the POS it uses the database connection owned by the POS (synchronized of course) to update the item database. Since you don't want to update a price in the middle of a sale, additional synchronization is used to block the update until the end of the sale.
To illustrate, I have a user interface written in JFC with a tree on the left and a configuration panel on the right. The tree holds configurable objects, such as the item file, POS dialogs and users. I choose an item from the tree, drop it on the configuration panel and make some changes (change the price of the an item or add a profile privilege to an operator). Then, through a pull-down menu, I can post the changes I make for delivery. This adds the current updates to a Vector. Then I can select an agent and assign a list of hosts to that agent. This particular agent understands that it has a list (Vector) of database items to deliver and update. Once I have selected the hosts, I press launch. The agent travels to the first host on the list, calls the update method for each database item and then goes on to the next host.
Security-minded readers are jumping up and down right now, and they are quite right. You will want to add additional security, such as signing your agents and securing the transport layer. You may also be able to assume that some level of network security exists in a closed enterprise WAN environment.
IBM has developed a much broader agent implementation they call aglets; information on aglets can be found at http://www.trl.ibm.com/aglets/. The project has recently been released as open source; see http://www.aglets.org/ for details.
Java and Linux in Retail?
If you have gotten this far you may be wondering how Java, and perhaps Linux, are faring in the retail world. The answer is, quite well. Several large retailers have made the move to Java; The Men's Wearhouse and Home Depot are both running Java in their stores today. Linux is doing well also. The classic argument against Linux, in particular the lack of a user interface, simply doesn't apply. In the context of retail POS, the application is the interface. Burlington Coat Factory has already rolled out Linux in all of their locations, both as a desktop and POS. So go buy a coat using Linux.
About the author: Quentin Olson lives on the Kitsap Peninsula in western Washington with his wife and two children. He works as a retail business systems consultant and systems integrator specializing in covert Linux installations. His open-source project described in this article has the distinction of being completely developed on a ferryboat. You can reach him at [email protected]
Copyright © 2001 Specialized Systems Consultants, Inc. All rights reserved. Embedded Linux Journal Online is a cooperative project of Embedded Linux Journal and LinuxDevices.com.
This article was originally published on LinuxDevices.com and has been donated to the open source community by QuinStreet Inc. Please visit LinuxToday.com for up-to-date news and articles about Linux and open source.