Posts Tagged XML

Connecting Java EE RESTful Web Services to Microsoft SQL Server Using NetBeans and GlassFish

Connecting Java EE RESTful web services, hosted on GlassFish, to Microsoft SQL Server – a high level overview. Demonstrate the creation of a Web Application project in NetBeans, including a SQL Server data source, entity classes from a SQL database, and RESTful web services. Show how to test and deploy the project to GlassFish.

Introduction

In a previous post, Connecting Java EE to SQL Server with Microsoft’s JDBC Driver 4.0, I demonstrated how Microsoft’s JDBC Driver 4.0 can connect Java-based RESTful web services to Microsoft SQL Server. In a more recent post, Calling Microsoft SQL Server Stored Procedures from a Java Application Using JDBC, I demonstrated the use of JDBC to call stored procedures from a Java application. In this post, I am going to offer a high-level, end-to-end overview on how to create and connect Java EE RESTful web services, hosted on GlassFish, to SQL Server. The goals of post are:

  • Demonstrate the use of Microsoft’s JDBC Driver 4.0 to connect a Java-based application to SQL Server 2008 R2;
  • Demonstrate the use of NetBeans to:
    • Create a SQL Server Data Source;
    • Create entity classes from the SQL Server database using the SQL Server Data Source;
    • Create RESTful web services using JAX-RS, which communicate with database, through the entity classes;
    • Compile and deploy the data source, entities, and services to GlassFish;
  • Test the RESTful Web Services locally in NetBeans, and once deployed, in GlassFish.

Setting up the Post’s Example

To demonstrate the example in this post, I have the follow applications installed, configured, and running in my development environment:

If you have any questions about installing and configuring the Adventure Works database, please refer to my post, Convert VS 2010 Database Project to SSDT and Automate Publishing with Jenkins – Part 1/3. The post takes you through creating a SQL Server 2008 R2 instance (entitled ‘Development’), installing the Adventure Works database, and creating a database user (‘aw-dev’). Also, refer to my earlier post, Connecting Java EE to SQL Server with Microsoft’s JDBC Driver 4.0, for directions on installing the Microsoft JDBC driver in the lib directory of GlassFish. This is necessary before following along with this demonstration.

Note, since I have NetBeans, SQL Server, and GlassFish all installed on a single computer, the URLs in several of the screen-grabs switch between ‘localhost’ and my computer’s actual name. Both references are interchangeable.

The Demonstration

Here are the high-level steps I will walk-through in this post:

  1. Confirm the SQL Server instance, database, and user are functioning properly;
  2. Create a new Web Application project in NetBeans;
  3. Create the SQL Server data source in the project;
  4. Create entity classes from the SQL Server database;
  5. Create RESTful web services using the entity classes;
  6. Test the web services locally in NetBeans;
  7. Build and deploy the project to GlassFish;
  8. Test the web services on GlassFish.

SQL Server

Using Microsoft’s SQL Server Management Studio, Quest’s Toad for SQL, or similar IDE, confirm the ‘Development’ instance (or whatever you have named your instance) of SQL Server 2008 R2 is running. Confirm the Adventure Works database is installed in that instance. Lastly, confirm the ‘aw_dev’ user can connect to the Adventure Works database and view and interact with all the database objects. Confirming these items will elevate many problems you might otherwise encounter when creating the data source, next.

SQL Server 2008 R2 AdventureWorks Database

View of the SQL Server Instance from SSMS. Note the : 1) Server, Instance, User, 2) Database, and 3) Schema

Create New Web Application Project

Create a new Java Web Application project in NetBeans. Name the project whatever you would like; I named mine ‘JdbcSqlWebSrvTest’. Select the correct GlassFish server instance and GlassFish domain where the project will be deployed. I am deploying my project to the default ‘domain1’ domain.

Creating a New Java Web Application Project in NetBeans - 02

Choose the Web Application Project-Type in NetBeans

Creating a New Java Web Application Project in NetBeans - 03

Provide a Project Name and Location

Creating a New Java Web Application Project in NetBeans - 04

Select the Target GlassFish Server and Domain Where the Project Will be Installed

Creating a New Java Web Application Project in NetBeans - 05

Include any Frameworks You Will Use with the Project

Creating a New Java Web Application Project in NetBeans - 06

View of the New Web Application Project in NetBeans

Create SQL Server Data Source and Entity Classes from the Database

Right-click on the project again and select ‘New’ -> ‘Other…’. From the list of Categories, select ‘Persistence’. From the list of Persistence choices, choose ‘Entity Classes from Database’. Click Next.

Creating New Entity Classes from a Database - 01

Choose ‘Entity Classes from Database’

Before we can choose which database tables we want from the Adventure Works database to create entity classes, we must create a connection to the database – a SQL Server Data Source. Click on the Data Source drop down and select ‘New Data Source…’. Give a Java Naming and Directory Interface (JNDI) name for the data source. I called mine ‘AdventureWorks’. Click on the ‘Database Connection’ dropdown, select ‘New Database Connection…’.

Creating New Entity Classes from a Database - 02

Database Tables View Before Data Source is Created

Creating a New Datasource for Entity Classes from a Database - 01

Name the Data Source

This starts the ‘New Connection Wizard’. The first screen, ‘Locate Driver’, is where we point the will instruct NetBeans to use the Microsoft JDBC Driver 4.0 for SQL Server Driver. Locate the sqljdbc4.jar file.

Creating a New Datasource for Entity Classes from a Database - 02

Locate the Microsoft JDBC Driver 4.0 for SQL Server Driver .jar File

On the next screen, ‘Customize the Connection’, input the required SQL Server information. The host is the machine your instance of SQL Server is installed on, such as ‘localhost’. The instance is the name of the SQL Server instance in which the Adventure Works database is installed, such as ‘Development’. Once you complete the form, click ‘Test Connection’. If it doesn’t succeed, check your settings, again.

Creating a New Datasource for Entity Classes from a Database - 03

Provide the SQL Server Adventure Works Database Connection Information

As I mentioned in an earlier post, the SQL Server Data Source forces you to select a single database schema. On the ‘Choose Database Schema’ screen, select the ‘HumanResources’ schema. The database tables you will be able to reference from you entity classes are limited to just this schema, when using this data source. To reference other schemas, you will need to create more data sources.

Creating a New Datasource for Entity Classes from a Database - 04

Select the ‘HumanResources’ Database Schema

Creating a New Datasource for Entity Classes from a Database - 05

New Data Source Complete with JNDI Name and Database Connection Created

Back in the ‘New Entity Classes from Database’ window, you will now have the ‘AdventureWorks’ data source selected as the Data Source. After a few seconds of processing, all ‘Available Tables’ within the ‘HumanResources’ schema are displayed. Choose the four tables shown in the screen-grab, below. Actually, two are database tables and two are virtual tables, called database ‘views’. We will need to do an extra step later on, to use the two entity classes built from the database views.

Creating New Entity Classes from a Database - 03A

Retrieving all the ‘HumanResources’ Schema Database Tables and Views

Creating New Entity Classes from a Database - 03B

All the ‘HumanResources’ Schema Database Tables and Views Available

Creating New Entity Classes from a Database - 04

Selecting the Database Views Throws a Warning Regarding no Entity IDs (Primary Keys)

On the next screen, ‘Entity Classes’, in the ‘New Entity Classes from Database’ window, give a package name to place the individual entity classes into. I chose to call mine ‘entityclasses’.

Creating New Entity Classes from a Database - 05

Provide the Name of the Package Where the Entity Classes Will be Created

On the next screen, ‘Mapping Options’, choose ‘Fully Qualified Database Table Names’. Without this option selected, I have had problems trying to make the RESTful web services function properly. This is also the reason I chose to create the entity classes first, and then create the RESTful web services, separately. NetBeans has an option that combines these two tasks into a single step, by choosing ‘RESTful Web Services from Database’. However, the ‘Fully Qualified Database Table Names’ option is not available on the equivalent screen, using that process (at least in my version of NetBeans 7.2). I prefer the two-step approach.

Creating New Entity Classes from a Database - 06

Select the ‘Fully Qualified Database Table Names’ Option

Click finished. You have successfully created the SQL Server data source and entity classes.

The data source we created, which will be deployed to GlassFish, is referred to as a JDBC Resource and a JDBC Connection Pool. This JDBC information is stored in the ‘glassfish-resources.xml’ file by NetBeans.

New Glassfish JDBC Connection Pool and Resource - Source (glassfish-resources.xml)

New GlassFish JDBC Connection Pool and JDBC Resource Contained in the glassfish-resources.xml File

The JPA Persistence Unit is found in the ‘persistence.xml’ file in the ‘Configuration Files’ folder. This file describes the Persistence Unit (PU). The PU serves to register the project’s four persistable entity classes, which are referred to by JPA as managed classes.

JPA Configuration - Design View (persistence.xml)

persistence.xml Design View

JPA Configuration - Source (persistence.xml)

persistence.xml Source View

RESTful Web Services from Entity Classes

Now that have a SQL Server Data Source and our entity classes, we will create the RESTful web services. Right-click on the project and select ‘New’ -> ‘Other…’ -> ‘Persistence’ -> ‘RESTful Web Services from ‘Entity Classes’. You will see a list of four entity classes we just created, from which to choose. Add all four entity classes.

Creating New RESTful Web Service from Entity Classes - 01

Creating the New RESTful Web Service from Four Entity Classes

Creating New RESTful Web Service from Entity Classes - 02

Add the Four Entity Classes

On the next screen, give a name for Resource Package to store the service classes in; I called mine ‘service’. That’s it; you now have four RESTful web services and the corresponding Enterprise Beans and Façade service classes. The service class sits between the RESTful web service and the entity class.

Creating New RESTful Web Service from Entity Classes - 03

Provide a Package Location to Place the RESTful Web Service Classes Into

Click finished. You have successfully created the RESTful web services.

Web Application Project with Entities and Services Added - 01

View of Web Application Project with Entity Classes and RESTful Web Services Added

Web Application Project with Entities and Services Added - 02

View of Web Application Project with Entity Classes and RESTful Web Services Added

Adding a Primary Key to Entity Classes

If you recall, I mentioned a problem with the two entity classes we created from the database views. To avoid an error when you build and deploy your project to GlassFish, we need to make a small change to the VEmployee.java and VEmployeeDepartment.java entity classes. Entity classes need a unique identifier, a primary key (or, Entity ID) identified. Since these two entity classes are built from database views, as opposed to database tables, they lack a primary key. To fix this, annotate the ‘businessEntityID’ field with ‘@Id’ in each class. This indicates that ‘businessEntityID’ is the primary key (Entity ID) for this class. The field, ‘businessEntityID’, must contain unique values, for this to work properly. NetBeans will make the suggested correction for you, if you allow it.

Fix Entity Id Error in View Entity Classes - 01

NetBeans Highlights the Entity Id Error in Two View-Based Entity Classes

Fix Entity Id Error in View Entity Classes - 02

Select the ‘businessEntityID’ Field as the Primary Key

Fix Entity Id Error in View Entity Classes - 03

View of Entity Class with ‘@Id’ Annotation Added

Test RESTful Web Services Locally in NetBeans

NetBeans provides an easy way to test the RESTful web services, locally. Right-click on the ‘RESTful Web Services’ project folder within the main project, and select ‘Test RESTful Web Services’. Select the first option, ‘Locally Generated Test Client’, in the ‘Configure REST Test Client’ pop-up window.

Test RESTful Web Service in NetBeans - 01

Choose the Local Test Client Option

NetBeans will open a web browser window and displays the RESTful URI (Universal Resource Identifier) for the services in a tree structure. There are four primary URIs, corresponding to the four services. Each primary URI has additional child URIs grouped under them. Are they URIs or URLs? I found this excellent post that does a very good job explaining the difference between the URL (how to get there) and the URI (the resource), which is part of the URL.

Test RESTful Web Service in NetBeans - 02

You May Get an ActiveX Warning When Using IE to Test the RESTful Web Services

Test RESTful Web Service in NetBeans - 03

View of all the RESTful Web Services Universal Resource Identifiers (URIs)

Click on the ‘entityclasses.employee’ URI. Choose the HTTP ‘GET()’ request method from the drop-down and click the ‘Test’ button. The service should return a status of ‘200 (OK)’, along with xml output containing information on all the Adventure Works employees. Congratulation, the RESTful web services have just returned data to your browser from the SQL Server Adventure Works database, using the entity classes and data source you created.

Test RESTful Web Service in NetBeans - 04

All Employees Being Successfully Retrieved from the Adventure Works Database

Click on the other URIs to familiarize yourself with the various default resources. Test the employee ‘from/to’ URI by inputting two parameters, test the ‘count’ URI, and try changing the MIME type where applicable from XML to JSON and observe the results.

Test RESTful Web Service in NetBeans - 05

A Single Employee Being Successfully Retrieved from the Adventure Works Database Using Input Parameter

Test RESTful Web Service in NetBeans - 06

Count of All Employees Being Successfully Retrieved from the Adventure Works Database

WADL

Note the link in the upper right corner of the above screens, labeled WADL: ‘http://[your_server_path]/JdbcSqlWebSrvTest/webresources/application.wadl’

The WADL (Web Application Description Language) file is the machine-readable XML description of the RESTful web service(s). The WADL file is to RESTful web services, as the WSDL (Web Service Definition Language) file is to non-RESTful, SOA-/SOAP-oriented web services. The WADL provides all the information you need to understand to the various RESTful web service’s resources, and how to call them using their URIs. According to Wikipedia, in the WADL file, ‘the service is described using a set of resource elements. Each resource has param elements to describe the inputs, and method elements which describe the request and response of a resource. The request element specifies how to represent the input, what types are required and any specific HTTP headers that are required. The response describes the representation of the service’s response, as well as any fault information, to deal with errors.’ You can download the WADL file (application.wadl), and review it in an XML-friendly viewer such as Notepad++.

View of WADL - application.wadl

View of the RESTful Web Services’ WADL File – application.wadl

Deploy Project to GlassFish

Now that the RESTful web services are working properly from within NetBeans, we can deploy them to GlassFish. To deploy the project to GlassFish, right-click on the main project icon in the Projects tab and select ‘Clean and Build’. Once the project builds successfully, right-click again and select ‘Deploy’. This will instruct Apache Ant to deploy the project as a .war file to GlassFish, using the project’s default Ant deploy task. The SQL Server data source will also be installed into GlassFish.

Once the deployment is complete, switch to GlassFish and refresh the home page if necessary. Under the ‘Applications’ item on the left-hand navigation menu, you should see a new application with the same name as your project, ‘JdbcSqlWebSrvTest’.

GlassFish 01 - Prior to Deploying New Application

Default View of GlassFish Domain Prior to Deploying the New Web Application

GlassFish 02 - New Application Deployed to GlassFish

New Web Application and JDBC Resource and Pool Deployed Successfully to GlassFish

Also, under the ‘JDBC’ -> ‘JDBC Resources’ item, you should see a resource with the same name as the data source you created in NetBeans, ‘AdventureWorks’. Under the ‘JDBC’ -> ‘JDBC Connection Pools’, you should see a pool entitled ‘microsoft_sql_AdventureWorks_aw_devPool’. The JDBC Resource, ‘AdventureWorks’, is linked to this pool. The pool is a ‘javax.sql.DataSource’ resource type, which references the ‘com.microsoft.sqlserver.jdbc.SQLServerDataSource’. This data source is identical to the data source you built in NetBeans.

GlassFish 03 - New Datasource (Resource) Deployed to GlassFish

New JDBC Resource Successfully Deployed to GlassFish

GlassFish 04 - New JDBC Connection Pool

New JDBC Connection Pool Successfully Deployed to GlassFish

Test Web Services on GlassFish

To test the RESTful web services from GlassFish, begin by clicking on the ‘JdbcSqlWebSrvTest’ application, under ‘Applications’ menu item. On the Applications page, click on the ‘Launch’ action link. GlassFish open a new web browser window, and presents you with two ‘Web Application Links’, one link is HTTP and the other, HTTPS. Click on the HTTP link. This should display the default index.jsp page’s ‘Hello World!’ message.

Test RESTful Web Service in GlassFish - 01

Default Response from Application

To call the service, append the current URL to match the resource URIs you used when testing the services in NetBeans. For example, to display all the employees again like you did in NetBeans, append the current URL, http://%5Byour_server_name%5D:%5Bport%5D/JdbcSqlWebSrvTest/, to include the following:

http://%5Byour_server_name%5D:%5Bport%5D/JdbcSqlWebSrvTest/webresources/entityclasses.employee

This URI should return the same xml content you observed when testing this same URI locally in NetBeans.

Test RESTful Web Service in GlassFish - 02

All Employees Being Successfully Retrieved from the Adventure Works Database

As another test, append the URI to also include the Id of a single employee, as follows:

http://%5Byour_server_name%5D:%5Bport%5D/JdbcSqlWebSrvTest/webresources/entityclasses.employee/2

This should cut the amount of data returned from the Adventure Works database to a single employee record.

Test RESTful Web Service in GlassFish - 03

A Single Employee Being Successfully Retrieved from the Adventure Works Database Using a Parameter

One last test, remove the number two from the URI and add the word ‘count’, as follows:

http://%5Byour_server_name%5D:%5Bport%5D/JdbcSqlWebSrvTest/webresources/entityclasses.employee/count

This time, you should see a single integer returned to the browser, representing the count of all employees in the database’s employee table.

Test RESTful Web Service in GlassFish - 04

Count of All Employees Being Successfully Retrieved from the Adventure Works Database

Conclusion

Congratulations, the Java EE RESTful web services have been successfully deployed to GlassFish. The services are connecting to Adventure Works SQL Server database, through the entity classes and data source, and returning data to your web browser! Next step is to create a RESTful web services client application, to display the data returned by the services and/or to perform CRUD operations on the database.

, , , , , , , , , , , , , , , , , , , ,

15 Comments

Connecting Java EE to SQL Server with Microsoft’s JDBC Driver 4.0

Connect Microsoft SQL Server to Java EE JPA and JAXB with MOXy technologies using Microsoft’s JDBC Driver 4.0.

A t-sql script to create the three database tables used in this post is available on DropBox. If you like DropBox, please use this link to sign up for a free 2 GB account. It will help me post more files to DropBox for future posts.

Recently, I read a very good five-part blog series, Creating a RESTful Web Service, by Blaise Doughan. The series of posts demonstrate the effective use of JPA, JAXB with MOXy (JPA Entities to XML), EJB, and JAX-RS. Although Blaise’s series was written in 2010, the information it has is still quite relevant and insightful. I was interested in working through the sample project the series is based on. However, I wanted to use Microsoft’s SQL Server instead of Oracle as the data source. In this post, I detail changes I made to the series’ example project to work with SQL Server, using Microsoft’s JDBC Driver SQL Server.

Blaise’s blog, Java XML & JSON Binding (Object-to-XML and object-to-JSON mapping using JAXB and EclipseLink MOXy), is located at blog.bdoughan.com. According to his blog, Blaise is the Team lead for the TopLink / EclipseLink JAXB & SDO implementations, and the Oracle representative on those specifications. His blog is overflowing with a wealth of information on a number of Java EE technologies, including Java Persistence Architecture (JPA), Java Architecture for XML Binding (JAXB), Enterprise JavaBeans (EJB), Java API for RESTful Web Services (JAX-RS), and Oracle’s GlassFish application server.

Microsoft SQL Server as Data Source

In my current position, I work regularly with Microsoft’s relational database, SQL Server 2008 R2. However, Blaise’s blog series example uses Oracle Database XE. Using the Java EE technology stack, but switching the data source to SQL Server was relatively easy using Microsoft JDBC Driver 4.0 for SQL Server. The 4.0 version of the driver was just released in March of 2012. It includes the ability to interface with Microsoft’s new SQL Server 2012, and is also backwards compatible to SQL Server 2005. Of course there are alternative third-party SQL Server drivers, including those from DataDirect, i-net software, and so forth. I chose to use Microsoft’s own JDBC driver.

Before connecting to SQL Server using Microsoft’s JDBC driver, you need to create a SQL Server database, mirroring the database schema in Blaise’s blog post. You can easily translate the Oracle, native PL/SQL TABLE CREATE scripts into T-SQL. Once you’ve set up the three database tables and related relationships, your database schema should resemble the database diagram displayed below.

SQL Server 2008 R2 Database Diagram

Customer Service Database Diagram

The only change I made was to designate the ID primary key column in the CUSTOMER table as an auto-incrementing IDENTITY column. This allows inserting of new customers in the CUSTOMER table without having to manually manage unique IDs. You will note some extra annotations in the Customer entity class if you make the identity column change.

Configuring Microsoft’s JDBC Driver with NetBeans

Microsoft’s JDBC driver downloadable install package has two JAR class libraries, one supports JDBC 3.0 for use with JRE version 5, and another supports JDBC 4.0 for JRE 6. I used the later JAR file, sqljdbc4.jar, to build database connections based on my project’s designated JRE.

I’m currently using NetBeans IDE 7.1.1. To create a database connection in NetBeans, on the Services tab, right-click on Databases, and select New Connection… If this is your first time using the Microsoft driver, under Locate Driver, Driver drop down list, select New Driver… In the New JDBC Driver window, select one of the driver’s two JAR files (see explanation above). There is only one Driver Class, chosen by default. You can change the name of the driver as it appears in driver list, or leave NetBeans’ default descriptive connection naming convention format:

jdbc:sqlserver://[SERVER_NAME]\[INSTANCE_NAME];databaseName=CustomerService [[USER_NAME] on [SCHEMA_NAME]]

Adding a New JDBC Driver in NetBeans

Adding a New JDBC Driver in NetBeans

After setting up the new JDBC driver, select Next to set up the details of the specific database instance and database you wish to connect to for your project. Following Blaise’s blog, connect to the Customer Service database created earlier. I chose to connect to the database by inputting my database server, instance, and database names. Alternately, you could connect using TCP/IP (assuming it’s enabled), by inputting your SQL Server’s network name or IP address, and the port you have configured for SQL Server connections. Your setup will be specific to your environment, but should look like the below.

Creating a New Database Connection in NetBeans

Creating a New Database Connection in NetBeans

One limitation of Microsoft’s JDBC driver is that it requires you to select a specific database schema, as part of the last step in the connection creation process. Each specific database connection will only display database objects from the selected schema. In a production environment, where you may have multiple schemas, this can add complexity to managing connections.

Selecting Default Database Schema

Selecting a Default Database Schema

When finished, you will see your newly created Microsoft SQL Server driver listed under Drivers. You will also see your newly created database connection, using the Microsoft JDBC driver, listed under Databases. From this point on, you can follow Blaise’s series, substituting the Oracle database connection with the new SQL Server database connection in the Persistence Unit’s JDBC Connection drop down menu.

Database Connections in NetBeans

List of Database Connections in NetBeans

If you decide to move beyond the scope of Blaise’s series’ example project, you may want the ability to call the methods and properties of the SQL Server driver, directly in your code. To do so, add the driver’s JAR file to your project by right-clicking on the Libraries folder of your project on the Projects tab, and select Add JAR/Folder… Again, I added the same JAR file, sqljdbc4.jar, for use with JRE 6. Then, import the driver’s class library packages as necessary, such as com.microsoft.sqlserver.jdbc.SQLServerDataSource, used to build a SQL Server data source.

Configuring Microsoft’s JDBC Driver with GlassFish

To host the example web service in the blog series, you also need to install Microsoft’s JDBC driver on your application server; in the case of Blaise’s series, Oracle’s GlassFish Server. To install the driver on GlassFish 3.1.2, right-click on the GlassFish server instance in the Servers tab in NetBeans and select View Domain Admin Console. GlassFish’s administration console should appear in your web browser, assuming GlassFish is installed and running. You can follow the blog’s instructions to set up the JDBC Connection Pool and JDBC Resource, selecting MicrosoftSqlServer from the Database Driver Vendor drop down menu. If this option is not available, make sure you installed the driver’s JAR file in GlassFish’s lib folder, as explained in the first blog of Blaise’s series, and try restarting GlassFish in NetBeans. Be sure to close the Admin Console in the web browser first.

Additional Capabilities

Microsoft’s latest JDBC Driver 4.0 provides the ability to easily connect to SQL Server with Java. Included with the driver installation package, is excellent documentation and both Java and JavaScript code examples. Using the documentation and supplied examples, many of the more advanced capabilities of SQL Server are readily accessible to Java developers. These advanced features including application security, transactions, working with stored procedures, and working with advanced data-types such as XML. I strongly urge you to read more of Blaise’s blog posts to learn more about advanced Java EE topics, as well as Microsoft’s documentation to learn about JDBC with SQL Server.

One notable change since Blaise’s posts is the addition of EclipseLink JAXB (MOXy) as a JAXB provider in the latest release of GlassFish 3.1.2. It is no longer necessary to add MOXy to GlassFish, as the blog series instructs for the earlier version of GlassFish 3.0.1.

, , , , , , , , , , , , , ,

8 Comments

Interactive Form Functionality on the Client-Side Using jQuery

Restaurant Menu and Order Form Preview

Restaurant Menu / Order Form Preview

Introduction

Many of us have used ASP.NET Web Forms for years, combined more recently with ASP.NET AJAX, to build robust web-solutions for our clients. Although Web Forms are not going away, it is also not the only technology available to ASP.NET developers to build web-solutions, or necessarily always the best. A developer’s ability to understand and implement multiple development technologies is critical to ensuring the best solution for the client.

Recently, the popularity of serious client-side development with JavaScript, jQuery, and AJAX has exploded. Much of the server-side processing required with ASP.NET Web Forms can easily be moved to the client-side with the help of increasingly sophisticated scripting tools such as jQuery and Ajax. The following article demonstrates and discusses a simple client order form, built using HTML, JavaScript, jQuery, AJAX, XML, and CSS. This example demonstrates many basic as well as some advanced capabilities of jQuery, including:

  • Asynchronous HTTP (Ajax) request to populate a drop-down menu with XML data
  • jQuery animation and CSS manipulation to enhance the client UI experience
  • Use of jQuery plug-ins, specifically FormatCurrency to format text
  • JavaScript and CSS minification to increase performance and obfuscate client-side code
  • Use of Content Delivery Networks (CDN) to further optimize performance through web caching

In this example, a user individually chooses products from a drop-down menu, inputs the desired quantity, and adds the selection to their order. The selections along with a subtotal of their costs are displayed in the order table. Items can be removed from the order and additional items added. The order’s total cost is updated and displayed as items are added and removed. All events are handled on the client-side, without any server-side processing. A working example of this form can be accessed here.

About the Code

The files which make up the web directory of the order form example are as follows: (2) versions of the HTML order form, (1) XML data file with menu items, (6) JavaScript files, and (2) versions of the Style Sheets. Shown below is the directory of those files as seen in Visual Studio 2008. All code for this article is available for download at on The Code Project.

Visual Studio 2008 Solution View

Visual Studio 2008 Solution View

The order form example comes in two flavors – an easy-to-understand, development-oriented copy (order_dev.htm), and a production-oriented copy (order_prd.htm), optimized for faster web-serving. The development version has all my JavaScript left in the bottom of the HTML file. The Style Sheet, jQuery library and FormatCurrency jQuery plug-in scripts are externally linked to non-minified sources. Conversely, the production version has the Style Sheet and all JavaScript externally-linked to minified files. I created two versions of the order form in order to compare the effects of optimization techniques on web-serving performance.

Code Optimization and Obfuscation

Using CSS Drive’s CSS Compressor online utility, I decreased the size of my externally-linked Style Sheet file by 26%. I selected the ‘Super Compact’ and “Strip ALL Comments’ options. Using Google’s Closure Compiler online utility, I decreased the size of my JavaScript by 43%. I selected the ‘Simple’ Optimization option. The more aggressive ‘Advanced’ option resulted in JavaScript errors. I did not select a Formatting option. According to the results from Firefox using Yahoo! YSlow, externally linking to minified copies of my Style Sheet and JavaScript files reduced the total size of the information sent to the browser from 175.8K to 79.9K, a savings of nearly 55%.

You can further test page performance by replacing the local link to the jQuery script file with a link to the minified copy of jQuery on Google’s Content Delivery Network (CDN). The current link is commented out within order-prd.htm. For an explanation of the advantages and disadvantages of using a CDN, I recommend Dave Ward’s post on Encosia.com, entitled 3 reasons why you should let Google host jQuery for you.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

jQuery IntelliSense in Visual Studio 2008

The obvious advantage of keeping the JavaScript in the HTML page, at least during development, is the ability to take advantage of IntelliSense in Visual Studio 2008 with jQuery. IntelliSense makes the jQuery learning process much quicker! See a good post on this topic, jQuery Intellisense in VS 2008, by Scott Guthrie, at ScottGu’s Blog. Note, as of the date of publication of this article, the latest version of jQuery to have the necessary ‘-vsdoc’ file available for use with IntelliSense was version 1.4.1. I used this for the development version of the example. The production copy uses a later, minified version of jQuery 1.4.2, which is notably faster than 1.4.1.

Placing the Order

The form contains a button to place the final order. In this example, pressing the button returns a simple JavaScript alert(), depending on the contents of the order. In actual production, the order page could submit form data to a secondary page or code-behind class (ASP.NET Web Forms) for order processing. Alternatively, data could be formatted and sent directly back to an XML file or to a database using Ajax. Order processing could be done on the client- or server-side, depending on the technology implemented.

Using the Code

The order page contains two HTML tables. One table holds the menu selection elements and the other table displays the current order. Since jQuery so eloquently handles all interactions within the UI, there is very little HTML code to write.

<table id="select">
    <caption>
        Menu</caption>
    <tr>
        <td>
            Qnt.:
        </td>
        <td>
            <input id="select_quantity" type="text" /> (*1-99)
        </td>
        <td>
            <select id="select_item">
                <option selected="selected">Select an Item...</option>
            </select>
        </td>
        <td>
            <input id="add_btn" type="button" value="Add" />
        </td>
    </tr>
</table>
<br />
<br />
<table id="order_cart">
    <caption>
        Order</caption>
    <thead>
        <tr>
            <th>
                Qnt.
            </th>
            <th>
                ID
            </th>
            <th>
                Description
            </th>
            <th>
                Price
            </th>
            <th>
                Subtotal
            </th>
            <th>
                Remove
            </th>
        </tr>
    </thead>
    <tbody>
    </tbody>
    <tfoot>
        <tr>
            <th colspan="4">
                Total:
            </th>
            <th id="order_total">
                $0.00
            </th>
            <th>
                <input id="order_btn" type="button" value="Order!" />
            </th>
        </tr>
    </tfoot>
</table>

<script src="scripts/jquery-1.4.1.js" type="text/javascript"></script>

<script src="scripts/jquery.formatCurrency-1.3.0.js" type="text/javascript"></script>

The JavaScript contained in order_dev.htm immediately precedes the closing </body> tag. Keeping the JavaScript at the bottom of the page whenever possible allows the CSS and DOM elements to load first. I have included a large number of comments detailing much of the functionality contained in each part of the JavaScript.

<script type="text/javascript">
    //Retrieve XML document and loop for each item
    jQuery(function($) { //just like $(document).ready()
        $.ajax({
            type: "GET",
            url: "data/menu.xml",
            dataType: "xml",
            error: function() {
                $("<p>Error loading XML file...</p>")
                .replaceAll("#order_form")
            },
            success: function(xml) {
                $(xml).find("item").each(fWriteXML); //must call function as var
            }
        });
    });

    //Populate drop-down box with XML contents
    var fWriteXML = function writeXML() {
        var id = $(this).attr("id");
        var cost = $(this).attr("cost");
        var item = $(this).text();
        $("#select_item")
        .append($("<option></option>")
        .val(id) //same as .attr("value", id))
        .html(item)
        .attr("title", cost));
    };

    //Add selected item to order
    $(function() {
        $("#add_btn").click(function() {
            var order_item_selected_quantity = $("#select_quantity").val()
            var selected_item = $("#select_item option:selected");
            var order_item_selected_id = selected_item.val();
            var order_item_selected_name = selected_item.text();
            var order_item_selected_cost = selected_item.attr("title");

            var pattern = new RegExp("^[1-9][0-9]?$"); //Select between 1-99 items
            //Do not proceed if input is incorrect
            if (pattern.test(order_item_selected_quantity) &&
                order_item_selected_cost != "") {

                //Calculate subtotal
                var order_item_selected_subtotal =
                    parseFloat(order_item_selected_cost) *
                    parseInt(order_item_selected_quantity);

                $("<tr class='order_row'></tr>").html("<td>"
                    + order_item_selected_quantity + "</td><td>"
                    + order_item_selected_id + "</td><td class='order_item_name'>"
                    + order_item_selected_name + "</td><td class='order_item_cost'>"
                    + order_item_selected_cost +
                    "</td><td class='order_item_subtotal'>"
                    + order_item_selected_subtotal + "</td><td>"
                    + "<input type='button' value='remove' /></td>")
                .appendTo("#order_cart").hide();

                $("#order_cart tr.order_row:last").fadeIn("medium", function() {
                    orderTotal(); //Callback once animation is complete
                });

                //Format new order item values to currency
                $("#order_cart td.order_item_cost:last").formatCurrency();
                $("#order_cart td.order_item_subtotal:last").formatCurrency();

                clickRemove();
                clearForm();
            }
        });
    });

    //Bind a click event to the correct remove button
    function clickRemove() {
        $("#order_cart tr.order_row:last input").click(function() {
            $(this).parent().parent().children().fadeOut("fast", function() {
                $(this).parent().slideUp("slow", function() {   //the row (tr)
                    $(this).remove();   //the row (tr)
                    orderTotal();
                });
            });
        });
    };

    //Clear order input form and re-focus cursor
    function clearForm() {
        $("#select_quantity").val("");
        $("#select_item option:first-child").attr("selected", "selected");
        $("#select_quantity").focus();
    };

    //Calculate new order total
    function orderTotal() {
        var order_total = 0;
        $("#order_cart td.order_item_subtotal").each(function() {
            var amount = ($(this).html()).replace("$", "");
            order_total += parseFloat(amount);
        });

        $("#order_total").text(order_total).formatCurrency();

        //Create alternating colored rows in order table
        $("#order_cart tr.order_row:odd").css("background-color", "#F0F0F6");
        $("#order_cart tr.order_row:even").css("background-color", "#FFF");
    };

    //Pretend to place order if it contains items
    $(function() {
        $("#order_btn").click(function() {
            if ($("#order_cart tr.order_row:last").length == 0) {
                alert("No items selected...");
            }
            else {
                alert("Order placed...");
            }
        });
    });
</script>

Additional Resources

I highly recommend the following resources to both beginner and intermediate jQuery developers who want to learn more about this great client-side development tool:

, , , , , , , ,

2 Comments