Java Web Store
— print (last updated: Nov 14, 2009) print

Select font size:
Download the WebStore.zip archive. This archive contains the Java source, web files and database setup files. You will need the database setup files regardless of whether you use NetBeans or Eclipse.

NetBeans JAR files

The JAR files relevant to this application are:
Hibernate essentials:
  antlr-2.7.6.jar
  commons-collections-3.2.1.jar
  dom4j-1.6.1.jar
  hibernate-core-3.3.1.GA.jar
  javassist-3.9.0.GA.jar
  jta-1.1.jar
  slf4j-api-1.5.8.jar
  slf4j-simple-1.5.8.jar
Commons/Lang:
  commons-lang-2.4.jar
MySQL JDBC Driver:
  mysql-connector-java-5.1.10-bin.jar
Password encryption: 
  commons-codec-1.4.jar
File upload: 
  commons-fileupload-1.2.1.jar 
  commons-io-1.4.jar
If you're using NetBeans, you'll need to make these available as JAR files to the application. You can grab the whole bundle as:
WebStoreLib.zip
You can add these JAR files per se to the project or add them to a new library. Alternatively, you can use: The "full library" distribution files for the commons selections are:
commons-lang-2.4-bin.zip
commons-codec-1.4-bin.zip
commons-fileupload-1.2.1-bin.zip
commons-io-1.4-bin.zip

Database Setup

This project uses the webstore database with multiple tables. We will assume the guest user with no password has been created. Go into the WebStore folder and run these:
$ mysql -u root < database.sql
$ mysql -u guest webstore < tables.sql
$ mysql -u guest webstore < tables-images.sql

Webstore Database Description

The database has five primary tables: products, admins, shoppers, orders, lineitems. The images table is intended be used for image uploads which can later be transferred to actual file images. In each case, the standard, primary key, integer id field is present in each, but not shown.
products:
 description TEXT
 name VARCHAR(100)
 imagefile VARCHAR(50)
 price DECIMAL(9,2)
admins:
 username VARCHAR(50)
 password VARCHAR(50)
 UNIQUE (username)

shoppers:
 username VARCHAR(50)
 password VARCHAR(50)
 UNIQUE (username)

orders:
 shopper_id INT
 created TIMESTAMP


lineitems:
 order_id INT
 product_id INT
 qty INT
 price DECIMAL(9,2)
images:
 imagefile
 data MEDIUMBLOB
 UNIQUE(imagefile)

The primitive entities are products, shoppers and admins. The "derived" data appears in the orders and items tables which contain foreign keys. The naming of foreign keys is consistently presented as
table-row-name_id
where table-row-name is the table name (always plural) made singular. In particular, an order is reflected as an entry in the orders table made by a shopper (shopper_id) at a certain time (created). The order consists of one or more line items represented in the lineitems table where each line item:

Hibernate mapping

We need a representation of a product's price and the other for uploaded image (binary) data. The former commonly uses a MySQL DECIMAL type, in this case DECIMAL(9,2), meaning 9 significant digits with 2 decimal places. We map this type to the Hibernate float type.

Binary data is usually represented as the MySQL "BLOB" type in one of three possible sizes: BLOB, MEDIUMBLOB, LONGBLOB. We choose the middle one and map this to the Hibernate binary type.

tables.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="models.Product" table="products"> <id column="id" name="id" type="int"><generator class="increment"/></id> <property name="name"/> <property name="imagefile"/> <property name="description"/> <property name="price" type="float"> <column name="price" sql-type="decimal(9,2)"/> </property> </class> <class name="models.CustOrder" table="orders"> <id column="id" name="id" type="int"><generator class="increment"/></id> <property name="shopper_id"/> <property name="created" type="timestamp"/> </class> <class name="models.LineItem" table="lineitems"> <id column="id" name="id" type="int"><generator class="increment"/></id> <property name="order_id"/> <property name="product_id"/> <property name="qty"/> <property name="price" type="float"> <column name="price" sql-type="decimal(9,2)"/> </property> </class> <class name="models.Admin" table="admins"> <id column="id" name="id" type="int"><generator class="increment"/></id> <property name="username"/> <property name="password"/> </class> <class name="models.Shopper" table="shoppers"> <id column="id" name="id" type="int"><generator class="increment"/></id> <property name="username"/> <property name="password"/> </class> <class name="models.Image" table="images"> <id column="id" name="id" type="int"><generator class="increment"/></id> <property name="imagefile"/> <property name="data" type="binary"/> </class> </hibernate-mapping>

Product Display


all_products.jsp
<%@ page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="db" scope="session" class="models.DB" /> <%@ page import="java.util.*" %> <%@ page import="org.hibernate.*" %> <%@ page import="models.*" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="css/common.css" type="text/css" /> <title>Product List</title> </head> <body> <h1>Product List</h1> <% Session sess = db.getSession(); Criteria crit = sess.createCriteria(Product.class); List<Product> products = crit.list(); %> <table cellpadding="5px"> <tr> <th>item</th> <th>name</th> <th>price</th> </tr> <% boolean stackDetails = true; for (Product product : products) { int id = product.getId(); String name = product.getName(); float price = product.getPrice(); %> <tr> <td><%=id%></td> <td><%=name%></td> <td>$<%=price%></td> <% if (stackDetails) { %> <td><a href="product.jsp?id=<%=id%>">details</a></td> <% } else { %> <td><a href="javascript:location.replace('product.jsp?id=<%=id%>')" >details</a></td> <% } %> </tr> <% } %> </table> </body> </html>

product.jsp
<%@ page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="db" scope="session" class="models.DB" /> <%@ page import="java.util.*" %> <%@ page import="org.hibernate.*" %> <%@ page import="models.*" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <% String id = request.getParameter("id"); int num_id = Integer.parseInt(id); Session sess = db.getSession(); Product product = (Product) sess.load(Product.class, num_id); String name = product.getName(); float price = product.getPrice(); String imagefile = product.getImagefile(); String description = product.getDescription(); %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="css/common.css" type="text/css" /> <link rel="stylesheet" href="css/product.css" type="text/css" /> <title><%=name%></title> </head> <body> <table class="display"> <tr> <td><h3 class="product"><%=name%></h3></td> </tr> <tr valign="top"> <td width="400px" class="img"> <img alt="<%=name%>" src="images/<%=imagefile%>" /> </td> <td style="padding-top:20px"> Price: $<%=price%> <br /><br /> Item: <%=id%> <br /><br /> <div style="padding:10px;border:solid 1px #c00"> <b>Add to Cart<br />(replace this)</b> </div> </td> </tr> <tr valign="top"> <td colspan="2" class="descrip"> <%=description%> </td> </tr> </table> </body> </html>

Admin Access

The significant change from the Web Books application is that we want to authenticate by username/password, instead of simply by password. In this case, it is common to store the relevant username/password pairs in a database table. To make things more realistic, the password is assumed to be encrypted using the SHA1 digest function readily available to MySQL.

Therefore our application must create a form which allows username/password input, take the username and see if it matches a database table record, and then compare the SHA1 encryption of the password with the encryption stored within the record.

admin.jsp
<jsp:useBean id="db" scope="session" class="models.DB" /> <jsp:useBean id="authadmin" scope="session" class="util.AuthInfo" /> <% if (authadmin.getUser() == null) { request.getRequestDispatcher("login.jsp").forward(request, response); return; } %> I'm an admininstrator:<br /> username: <%= authadmin.getUser() %><br /> id: <%= authadmin.getId() %>

login.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Admin Login</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="css/common.css" type="text/css" /> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/jquery.form.js"></script> <script type="text/javascript" src="js/errors.js"></script> <script type="text/javascript" src="js/login.js"></script> </head> <body> <h1>Admin Login</h1> <form id="auth" action="AdminValidate" autocomplete="off"> <table cellpadding="5px"> <tr> <td>Username: </td> <td><input name="username" type="text" size="15" /></td> </tr> <tr> <td>Password:</td> <td><input name="password" type="password" size="15" /></td> </tr> </table> <input type="submit" value="Enter" /> </form> </body> </html>

admin_logout.jsp
<jsp:useBean id="authadmin" scope="session" class="util.AuthInfo" /> <% authadmin.setUser(null); response.sendRedirect("."); %>

login.js
$(function() { $('#auth').ajaxForm({ success: function(data) { if (data) { alert(data) } else { location.reload(); } } }) })

util.AuthInfo
package util; public class AuthInfo { private String user = null; private int id; public AuthInfo() { } public String getUser() { return user; } public int getId() { return id; } public void setUser(String user) { this.user = user; } public void setId(int id) { this.id = id; } }

servlet.AdminValidate
package servlet; import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; import org.hibernate.*; import org.hibernate.criterion.*; import java.util.*; import org.apache.commons.codec.digest.DigestUtils; import models.*; import util.AuthInfo; public class AdminValidate extends HttpServlet { protected void processRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { String username = request.getParameter("username").trim(); String password = request.getParameter("password"); DB db = (DB) request.getSession().getAttribute("db"); if (db == null) { db = new DB(); request.setAttribute("db", db); } Session sess = db.getSession(); Criteria crit = sess.createCriteria(Admin.class); crit.add(Restrictions.eq("username", username)); List<Admin> list = crit.list(); if (list.size() == 0) { out.print("Invalid User"); return; } Admin admin = list.get(0); String encrypted_password = DigestUtils.shaHex(password); if (!admin.getPassword().equals(encrypted_password)) { out.print("Invalid Password"); return; } AuthInfo authadmin = (AuthInfo) request.getSession().getAttribute("authadmin"); authadmin.setUser(username); authadmin.setId(admin.getId()); } finally { out.close(); } } // ... }

Order Display


order_display.jsp
<%@ page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="db" scope="session" class="models.DB" /> <%@ page import="java.util.*" %> <%@ page import="org.hibernate.*" %> <%@ page import="org.hibernate.criterion.Restrictions" %> <%@ page import="models.*" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="css/common.css" type="text/css" /> <title>Sample Order</title> </head> <body> <h1>Sample Order</h1> <% String id = request.getParameter("id"); int num_id = Integer.parseInt(id); Session sess = db.getSession(); CustOrder order = (CustOrder) sess.load( CustOrder.class, num_id ); Criteria crit = sess.createCriteria(LineItem.class); crit.add(Restrictions.eq("order_id", num_id)); List<LineItem> items = crit.list(); %> shopper: <%= id %> (should be the user name)<br /><br /> date: <%= order.getCreated() %> (better date display)<br /><br /> <table cellpadding="5px"> <tr> <th>item</th> <th>name</th> <th>quantity</th> <th>price/item</th> <th>price</th> </tr> <% for (LineItem item : items) { int product_id = item.getProduct_id(); int qty = item.getQty(); float price = item.getPrice(); %> <tr> <td align="center"><%=product_id%></td> <td>--add--</td> <td align="center"><%=qty%></td> <td align="right">$<%=price%></td> <td align="right">$<%=qty * price%></td> </tr> <% } %> <tr> <th colspan="4" align="right">total:</th> <td align="right">$...</td> </tr> </table> price and subtotal should have correct currency format </body> </html>

Image Upload/Display

This part of the application suggests how images might be provided for our products. We want the images to be actual files, but a web application is often incapable of actually creating a file due to insufficient access rights to do so.

In contrast, the web application can easily be given the ability to upload a file, even an image, into a database table. Our code then provides the ability to "view" such database-stored images (perhaps by an adminstrator) and then store them directly into some target directory.

upload_image.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="db" scope="session" class="models.DB" /> <jsp:useBean id="authadmin" scope="session" class="util.AuthInfo" /> <% if (authadmin.getUser() == null) { request.getRequestDispatcher("login.jsp").forward(request, response); } %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> <link rel="stylesheet" href="css/common.css" type="text/css" /> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/jquery.form.js"></script> <script type="text/javascript" src="js/errors.js"></script> <script type="text/javascript" src="js/upload.js"></script> <style type="text/css"> input.text { font-size: 11pt; background-color: #edf; font-weight:bold; } </style> </head> <body> <h2>Image Upload</h2> <form id="upload" enctype="multipart/form-data" method="post" action="ImageUpload"> image file: <input class="text" type="file" name="filename" size="70" /> <br /><br /> <input type="submit" value="Upload" /> </form> </body> </html>

upload_show.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="db" scope="session" class="models.DB" /> <%@ page import="java.util.*" %> <%@ page import="org.hibernate.*" %> <%@ page import="models.*" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> <link rel="stylesheet" href="css/common.css" type="text/css" /> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/jquery.form.js"></script> <script type="text/javascript" src="js/errors.js"></script> <script type="text/javascript" src="js/upload.js"></script> <style type="text/css"> img { border: solid 1px black; } </style> </head> <body> <h2>Show Uploaded Image</h2> <p>Select and show image, save it into your "web images folder", then delete it.</p> <% Session sess = db.getSession(); Criteria crit = sess.createCriteria(Image.class); List<Image> images = crit.list(); %> <select id="imgid"> <% for (Image image: images) { int id = image.getId(); String name = image.getImagefile(); %> <option value="<%=id%>"><%=name%></option> <% } %> </select> <button id="show">Show</button> <button id="delete">Delete</button> <br /><br /> <div id="out"></div> </body> </html>

upload.js
$(function() { $('#upload').ajaxForm( function(data) { if (data != "SUCCESS") { alert(data) return } alert("Success!") $("#upload input[name=filename]").val("") } ) $("#delete").click(function(){ alert("not implemented") }) $("#show").click(function(){ $("#out").html("<img src='ShowImage?id=" + $("#imgid").val() + "' />") }) })

servlet.ImageUpload
package servlet; import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache.commons.fileupload.*; import org.apache.commons.fileupload.disk.*; import org.apache.commons.fileupload.servlet.*; import org.apache.commons.io.FilenameUtils; import org.hibernate.*; import models.*; import util.AuthInfo; import java.util.*; public class ImageUpload extends HttpServlet { protected void processRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { AuthInfo authadmin = (AuthInfo) request.getSession().getAttribute("authadmin"); if (authadmin == null || authadmin.getUser() == null) { throw new Exception("unauthorized usage"); } if (!ServletFileUpload.isMultipartContent(request)) { throw new Exception("unintended usage"); } FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); List items = upload.parseRequest(request); FileItem item = (FileItem) items.get(0); String name = item.getName(); if (name.equals("")) { throw new Exception("Unnamed image file"); } name = FilenameUtils.getName(name); // omit the full path, if sent (IE) byte[] data = item.get(); DB db = (DB) request.getSession().getAttribute("db"); if (db == null) { throw new Exception("unintended usage: db undefined"); } Image image = new Image(name,data); Session sess = db.getSession(); sess.getTransaction().begin(); sess.save(image); sess.flush(); sess.getTransaction().commit(); out.print("SUCCESS"); } catch (HibernateException x) { out.print(x.getCause()); } catch (Exception x) { out.print(x); } finally { out.close(); } } // ... }

servlet.ShowImage
package servlet; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.hibernate.*; import models.*; public class ShowImage extends HttpServlet { protected void processRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { String id = request.getParameter("id"); int num_id = Integer.parseInt(id); if (id == null) { throw new Exception("unintended usage"); } DB db = (DB) request.getSession().getAttribute("db"); if (db == null) { db = new DB(); request.setAttribute("db", db); } Session sess = db.getSession(); Image image = (Image) sess.load(Image.class, num_id); byte[] data = image.getData(); String name = image.getImagefile(); response.setContentType("image/jpeg"); response.setHeader("Content-Disposition", "filename="+name); ServletOutputStream ostr = response.getOutputStream(); BufferedOutputStream bstr = new BufferedOutputStream(ostr); bstr.write(data, 0, data.length); bstr.close(); } catch (Exception x) { PrintWriter out = response.getWriter(); response.setContentType("text/html;charset=UTF-8"); out.println(x); out.close(); } } // ... }


© Robert M. Kline