Thursday, July 19, 2012

JBoss Toy Store Screencast

In a previous blog, I wrote an introduction to the various components included in the JBoss Toy Store - that is the name we gave our demo application for JBoss World 2012's Keynote address. The name "toy store" was because we were allowing our "sales staff" to acquire/giveaway cool electronic gadgets or JBoss "schwag" like t-shirts and coffe mugs. I recently sat down and recorded a screencast of the demo application, making it much easier to see and hopefully easier to following than the recording of the staged event. Your feedback and comments are always welcome - plus, the JBoss Toy Store codebase is open source, I would love to see others forking it on github, making improvements, running the demo at their local JUG with a room full of attendees and then making contributions of code, videos, blogs, etc.

JBoss Toy Store Demonstration from JBoss Developer on Vimeo.

And if you are interested in JBoss technical content, we have over 150 videos hosted at

Monday, July 2, 2012

JBoss World Keynote Demo 2012

In June 2011, we focused our keynote demonstration on JBoss' AS7 and Infinispan technology which we delivered as fully supported products (Enterprise Application Platform 6 and JBoss Data Grid 6) this year.

For 2012, we wanted to make a killer demonstration that was more accessible today - leveraging a core technology that is already officially supported - jBPM 5 in JBoss BRMS 5.3.   The BPM landscape, normally only accessible by large corporations due to license costs and complex proprietary software changing.    This blog provides a high-level overview for the various elements that went into the making of the demonstration.   We have already opensourced the codebase at JBoss Developer Framework (jdf).

Recording of the JBoss World 2012 Keynote on YouTube.  The demonstration starts at 14:33.

- BPMN2 modeling (jBPM & Drools - BRMS 5.3)
- Mobile Tasks - HTML5 based task interactions (Aerogear)
- Enterprise Application Provisioning (AppBlade)
- Real-time web-based dashboard (Errai)
- Cloud/Platform-as-a-Service (OpenShift)
- Gamification 
- Open Source - makes all of this technology more accessible to all developers

BPMN2 Web Designer

BPMN2 + Business Process, Rules & Events

With jBPM & Drools, we are able to model the core business logic via BPMN2 and a decision table using a web-based repository and modeling tool.  This means the workflow and its associated rules can easily be changed, in production, without programming and without service disruption.

HTML5 + Cordova Application

AppBlade Provisioning

HTML5, Apache Cordova & AppBlade

We then extended the business logic layer via RESTful services that were consumed by a HTML5 application wrapped with Apache Cordova and delivered via AppBlade - the application was to enable "employees" to use a simple catalog and shopping cart to order schwag (promotional items, giveaways), we had a little sales contest, encouraging audience members to pound in orders via the AppBlade provisioned application or the mobile web using their smartphone's browser.

Leaderboard - Errai-based Dashboard

As a live, audience-interactive demonstration, we needed a "dashboard" that could tell us, instantly, how a particular participant was performing - much like any organization would love to have to real-time analytics - we took advantage of Drools Fusion (BRMS 5.3), eventing and then leveraged Errai+GWT to push those events out to a waiting web browsers running on Linux, Mac and Windows.  Our dashboard is also an HTML5 application that can leverage the current capabilities of advanced web browsers.   This was a great risk - we had several hundred people in the audience, with a saturated WIFI and leveraging free resources at OpenShift.   Our Errai-based Leaderboard turned out to be a huge hit!  And it clearly demonstrates how the average web browser can be used for real-time process monitoring and it runs great on the iPad.

Not only do we believe that HTML5 and the explosion of mobile devices is having a profound impact on the BPM industry, PaaS, Platform-as-a-Service, will enable many more organizations who could not afford a large IT capital expenditure to roll-out a new system to leverage business rule and business process systems.  We also needed to leverage the cloud for a purely practical reason - we were enabling random individuals using their 3G/4G (or conference WIFI) mobile devices - this meant the system had to have a public IP address - OpenShift was the answer.

We were doing a live, in-person event, where we required audience participation for the software to even function properly.  No volunteers, software that was too hard to use or simply the lack of motivation, would have completely changed the dynamic of the demonstration.   The leaderboard was the secret ingredient - players were able to see their name on the board and instantly see how their activities on their phones were impacting the business scenario.   The concepts of gamification for BPM can obviously get much more sophisticated than what you see in our demo, however, the core concepts are there - make it competitive, recognize and reward the winner, build a team to play along and acknowledge and reward as many random players as possible - encouragement & motivation.  Perhaps in a future version we would offer badges!

Open Source
Not only is are the key products behind this demonstration open source, the actual demo code is itself also open source - we hope that many of you grab the codebase and run this same presentation at your local JUG - run it on OpenShift - have your audience break out their phones and give it a try!

A huge thank you to Kevin Conner who served as a our overall architect and engineering lead for the project.   All of us took this project on knowing that the actual products upon which the demonstration was based was also being delivered in the same month.  It was an exciting challenge and we had a fantastic team - now we get a weekend off and it is time to start planning our next crazy demo - bring on the robots and gesture UI.  ;-)

Monday, April 23, 2012

My Getting Started Experience/Tutorial with Appcelerator Titanium 2.0 and Appcelerator Cloud Services (formerly known as CocoaFish)

First, I am a complete newbie when it comes to Appcelerator though I am comfortable with JavaScript and cloud related technologies.  I have spent time with Azure (C# via Visual Studio), Cloud Foundry, Heroku, Google App Engine, Amazon's Beanstalk and of course Red Hat's OpenShift as PaaS (platform-as-a-service) offerings.
And I have been able to start digging into various client-side development technologies like jQuery Mobile + PhoneGap/Apache Cordova (see Kris' article) and just recently I tried out some Objective-C via XCode and the fantastic Kinvey quickstart tutorial.

Recently, I have had several people I know and respect tell me that Appcelerator Titanium is the best solution for cross-platform mobile application development, it uses JavaScript as its primary language therefore the average jQuery front-end to Java back-end guy like myself should be able to learn it easily.  Plus, Appcelerator Titanium is based on Eclipse which is something I have spent a lot of time with for my JBoss "day job" as evidenced by all kinds of screencasts.

My specific interest in mobile is mostly related to how to communicate with a "backend" - and now it seems the term "BaaS" for backend-as-a-service is starting to become more popular with several offerings now available such as Parse, Kinvey, and StackMob.   The primary difference between a BaaS vs a PaaS is that BaaS assumes the developer wishes to focus only on his/her client-side code written in Objective-C, Android Java, JavaScript,  HTML5 + a JS lib (e.g. Backbone) - all the user management, data & blob storage, push notifications, etc are all managed through a client-side API and a web console.  In other words, a BaaS has no server-side coding to worry with.   A PaaS on the other hand, assumes the developer wishes to write custom server-side logic in PHP, Perl, Java, JavaScript for Node.js, etc.

On April 17. 2012, Appcelerator announced Titanium 2.0 where the most important feature (IMHO) is integration with the recently acquired CocoaFish - cloud services for the mobile developer, now called Appcelerator Cloud Services.

And here it is April 21st and I decided to spend my Saturday giving it a try and writing up this blog. :-)

Keep in mind that my primary goal is to be able to store & retrieve data associated with my mobile application "in the cloud" and ideally, share data between multiple instances (multiple devices) of my application.  Unfortunately, that particular use case does not have a tutorial and what follows are the basic steps that I followed to get some simple things working.

Note: There are a few documents that I found to be useful:

and there are some examples that are installed on your local machine.  In the case of a Mac, you might be able to find them at:

$HOME/Library/Application Support/Titanium/modules/commonjs/

For this particular weekend (April 21st),  I was unable to find a "from scratch" tutorial, so I had to dig around and follow some hunches.   Hopefully this blog post inspires the good folks at Appcelerator to provide a nice getting started/tutorial for ACS with a Titanium client application.

1) Register at - this was a little bit tricky, I had originally went to "" which redirected me to and from there I clicked on the Start Using Cloud Services For Free button.  This was a bit awkward as it had me fill in a form and await an email verification, it then would ask me to login, sign-up, login again.  After about four attempts at the login it seemed to feel that I belonged and settled down.  It could be I was just trying to move to fast for the system.  Preserving pays off - just login a couple of more times. :-)  Why did I start with "cocoafish"? Because I did not read the press release until after I attempted to sign up, so I did not really know what the new name/website was.

2) Download Titanium Studio - I won't bore you with the details of download & installation as it is very straightforward - and I had previously installed Titanium Studio but never really used it - so upon launching it this past Friday night - it automatically knew the 2.0 update was ready - I just followed the typical Eclipse update process and was painlessly upgraded.  I do believe you will need XCode and the iOS SDK installed on your machine as well and I already had that installed from previous attempts at using PhoneGap and Objective-C.

3) Back at the ACS web console, I created a ACS only application called ToDo - why did I choose ACS only and name it ToDo?  I really cannot say, it was somewhat random and "felt right".

Then I went back to Titanium Studio to begin the development project - perhaps it was possible to create the ACS App from within Studio but I did not/have not notice it - so my process is to build both halves, client & server and then connect the two.

4) File -> New -> Titanium Mobile Project

Project name: CloudUsers (note: screenshot shows CloudUsers2 because I ran through this twice)
App Id: com.yourcompany.cloudusers (note: I am Java guy so I used a "package name")
Company/Personal URL:
and Automatically cloud-enable this application should be checked by default.

Select Next

Select Single Window Application

and Select Finish

Note: After working with several cloud offerings, I was expecting that the New wizard would prompt me for my ACS app - ideally provide a list of the apps described in the ACS web console.   The next step is how I addressed this disconnect between my client-side app and the ACS service.

5) Edit tiapp.xml to inject your ACS App's keys.  You will wish to match up the various ACS keys with what is in the web console.  Unfortunately, the web console and this XML file have slightly different names for each key - that can be an issue for the newbie.

"acs-api-key-development"= APP Key
Note: double clicking on the APP Key data does not highlight it correctly in the web console.   So be a little cautious with your highlight to copy & paste - double clicking tends to also grab the string "Key".

In the top right-hand corner of the web console, there is an option for PRODUCTION and DEVELOPMENT, make sure DEVELOPMENT is selected for the "acs-api-key-development" property. Then click on PRODUCTION to get it's APP Key.

Click on Show OAuth Credentials

"acs-oauth-key-development"= OAuth Consumer Key
"acs-oauth-secret-development" = OAuth Secret
You can follow the same basic steps to get the Production keys.

Save your changes to tiapp.xml 

6) Create some users, you can not create data (custom objects) without first having a user.   Open up ui/common/FirstView.js and modify it so you have several textfields and a button - to create a new user.  First, declare the Cloud variable to interact with ACS:

var Cloud = require('');
Cloud.debug = true;

I placed these lines directly inside of "function FirstView", perhaps they should have gone elsewhere but I am brand new to Appcelerator and it worked fine in this location.

Then add a series of textfields, I found that if you type in "text" and hit Control-Space, Titanium Studio will then drop in a generic declaration of a Ti.UI.createTextField block as well as an eventlistner.  This is a tremendous aid to developer productivity.  Just note that there are some odd things about this content assist feature:
a) A textfield comes with the following property set:
softKeyboardOnFocus : Ti.UI.Android.SOFT_KEYBOARD_DEFAULT_ON_FOCUS
which causes a runtime error in the iOS simulator, you will need to comment it out.
Ideally, Titanium Studio would recognize that my project is not setup for Android and would leave that property out.
b) Any component added via this trick "text" with Control-Space or "button" with Control-Space, creates a particular line of code to add the component to the overall "window":
"parentView" needs to be changed to "self" for these Single Window Applications.
c) If you use "button" and Control-Space, it creates a button with myHeight, myWidth, myTop and myLeft - those are not valid values - you will need to change them to numbers.  Leaving those variable names in your project will result in a nice big red error message.

The fields you need for a new user form are:

and then you will need a button, in my case, I called it "Create"

    // Listen for click events.
    Create.addEventListener('click', function() {
        Cloud.Users.create ({
            username: userNameField.value,
            password: passwordField.value,
            password_confirmation : passwordConfirmation.value,
            first_name: firstName.value,
            last_name: lastName.value,
            email: emailAddress.value
        }, function (e) {
            if (e.success) {
            } else {

Complete listing

7) Test - Hitting the small green arrow in the Titanium Studio toolbar will launch the iOS Simulator (on a Mac, not sure what happens on a Windows machine).

Enter some data in the fields that should be valid and hit the Create button, I am not sure if invalid data (e.g. poorly formed email address) will be rejected by ACS.  

And then check out the ACS web console for your application - as you create new users.
and drill-down on "Users"

Like magic, your  mobile application's, client-side code is able to modify server-side resources - with no server-side coding required.

8) Now that you have some users in the system, you can create custom objects with the following block of code:
  label.addEventListener('click', function(e) {
      login  : 'username3',
      password: '123password',
    }, function(e) {
      if (e.success)   {

          classname : 'cars',
          fields : {
            make : 'Ford',
            color : 'green',
            year : 2010
        }, function(e) {
          if(e.success) {
            var car =[0];
            alert('Success:\\n' + 'id: ' + + '\\n' + 'make: ' 
            + car.make + '\\n' + 'color: ' + car.color + '\\n' + 'year: ' 
            + car.year + '\\n' + 'created_at: ' + car.created_at);
          } else {
            alert('Create Error:\\n' + ((e.error && e.message) || JSON.stringify(e)));
        }); // Cloud.Objects.create
      } else {
        alert('Login Error:\\n' +
                ((e.error && e.message) || JSON.stringify(e)));
    }); // Cloud.Users.login
  }); // label.addEventListener

And the result of running this code will create new custom objects which are visible in the ACS web console.
and when you drill-down on Custom Objects

Now, one trick that I use is to start new projects to test out new things.  In the case of adding a custom object, I made yet another Titanium Mobile Project following the same steps as the one for CloudUsers.

Warning: If you do not first login then you will receive a runtime error of "401: You need to sign in or sign up before continuing" 

This error message can be a real head scratcher.   Searching in Appcelerator's Q&A (pitiful attempt at forum) does not yield great results (I have a screenshot of that as well).   It is also funny that the documentation tells you how to put in the Error message but the result has the "\n" in the string.   

9) Querying for the custom objects does not actually require a user/login - I was able to query in a completely different project using the following block of code in FirstView.js

function FirstView() {
  //create object instance, a parasitic subclass of Observable
  var self = Ti.UI.createView();
  var Cloud = require('');
  Cloud.debug = true;
  // Create a Button.
  var Query = Ti.UI.createButton({
    title : 'Query Cars',
    height : 35,
    width : 100,
    top : 50
  // Listen for click events.
  Query.addEventListener('click', function() {
      classname: 'cars',
      page: 1,
      per_page: 10
    }, function(e) {
      if (e.success) {
        alert('Success:\\n' +
                'Count: ' +;
      } else {
           alert('Error:\\n' +
                ((e.error && e.message) || JSON.stringify(e)));        
      } // else - fail
    }) // Cloud.Objects.query
  // Add to the parent view.
  return self;

With this level of effort, one real day of digging in, including writing up this long blog post. I think I could finish my little application with Appcelerator.   My 14 year old son is more interested in using Objective-C and ACS does have a iOS SDK for that style of development.   You might see a future blog post where we compare our experiences with building a mobile based apps using both Objective-C and Appcelerator with the same backend services.

Project Sources

Why did I put all of this into a blog?  I may not get back to looking at ACS and Appcelerator Titanium for several days or weeks - so this posting will help jog my memory and it might be useful for someone else who is trying to get started with Appcelerator Cloud Services

Tuesday, April 17, 2012

StartupWeekend with jQuery Mobile, Node.js and MongoDB

I recently participated in the Triangle Startup Weekend, one of many happening around the globe on April 13th -15th as part of the overall   An organization whose goal is to spark entrepreneurship and what follows is my personal experience.  Bottom line: everybody with passion & energy should give this a try.   It was a great (and exhausting) experience.

This is an insane event where a team of strangers has to meet on Friday, where someone has the spark of an idea, organizes instantly and attempts to build a business plan, a go to market strategy and an initial MVP - minimum viable product by Sunday before lunch.  The majority of the initial ideas involve some form of web or mobile product and that is what interested a technologist like myself.  There were hundreds of people at the event, over 50 "founders" with initial ideas who had 60 seconds to pitch to the overall crowd.  The audience voted on those ideas - narrowing them down to a top 20.  Those idea generating founders were then told, form your team, get to work - final presentations are due on Sunday by noon - the chaos begins!

There is not even time to learn your teammates last names and you end up remembering folks as "marketing guy", "design girl" or "kid coder".

I have always been a big believer in that you need to be moving to be learning. And when your time to market as been reduced to a few hours then getting up to pace is critical - get your product to market instantly.

We formed "Team Truxie" on Friday night, with the initial team meeting around 10pm when we had made grabbed space in a NCSU classroom.  The team founders (guys with the idea) attracted a great team - a group that covered business leadership, marketing talent, design and development.   We had about 10 minutes to discuss the business problem that we were trying to solve and about 20 minutes to figure out who had what skills.

That was when one of our founders (Ed) dropped the bomb on us - we needed our product to be in production by 6pm on Saturday, forget noon on Sunday, we had less than 16 hours to build a working mobile application that we would put in front of all the other competitors.  And this was one of the secrets to our success - MVP - that minimum viable product - where you can get a very clear understanding of who your potential customers are and what those customers are willing to pay for.    Luckily for me, I had spent many hours in the book just a few months ago and it still sits beside my bed - The Lean Startup by Eric Ries.  If you are a professional software developer with an interest in business, this book will appeal to you.   My interpretation of the Lean Startup is that the most important goal is learning, product & business model experimentation that drives learning and that you need to iterate rapidly.

A great example from Lean Startup, did you know that you needed DropBox before you saw it?  Well, how do you put your product in front of people who must SEE it to GET it?  The demonstration is the best explanation - that creates the best feedback.

On Saturday evening at 6pm, we had the opportunity to turn all of our competitors into potential customers - the (Ed & Rob) business idea was to automate food trucks - basically mobile restaurants.   And one was arriving at the Triangle Startup Weekend event to feed the contestants that Saturday.

To that end, our founders also saw this as a learning opportunity and other members of the newly founded team understood that Startup Weekend is a competition.  The team immediately coalesced around that basic truth - Startup Weekend is a competition, it has rules that are described in a book that defines MVPs, pivots and validation (validated learning), everything most be honed for the 5 minute final presentation.

With this understanding, the team split into two groups - the business folks focused on the business plan, who are the customers, what are their major pain points, pricing models, marketing strategy, scalability, defensibility etc.  This is a critical element of Startup Weekend - can your team identify potential customers and put your idea and your product in front of them?

Ed (founder numero uno), as a programmer himself, had the idea that we should learn some new technology - he had initially started prototyping with JQuery Mobile (+Phonegap) with a Node.js + Express + MongoDB backend before the event and he already had worked out a contract with Rackspace for hosting our application.  Now this was the real trick, the developers joined up because that sounded like cool tech to work with - but no one on the team had used jQuery Mobile nor Node.js in production before - it would be fun, right?  :-) At best, a couple of us had worked through a tutorial or two.  For myself, I had just built a simple demo application using JQuery Mobile to talk to a JAX-RS endpoint hosted on JBoss the week before.  That demo application became the proof-point that illustrated something could be done quickly.  Besides, I had been preaching HTML5 style application development for some time now and it was an opportunity to put my thoughts to the test.

The pressure cooker - we had to build an app, deploy it and put it in front of...our fellow Startup Weekend participants in a few hours.   It is hard to describe how challenging it is to meet your fellow developers for the very first time, understand the business problem, identify the key priorities, consider the architecture, setup people's workstations for sharing files, split up tasks, etc.   And every possible corner than can get cut does - because the goal is the MVP - the barest minimum that impresses your audience, establishes the vision of the product and for the features it implements, it just works.  We could have attempted this with "paper prototypes" or simple mock-ups but for one of the key elements of the overall competition - how fast and how well can the team "gel".   And in our case, the core development team (Chris, Corey, Andrew and myself) came together fast.  I have never met any of those guys before that Friday night.  And by mid-afternoon Saturday, I can honestly say that that team could build most any web-based (or mobile web-focused) application needed by most businesses.   A startup's dream - an engineering team that can learn new technology and can crank out value immediately.

Granted, many of you reading this blog may have more than a couple of hours of experience with JQuery Mobile & Node.js - but our team did not have that level of experience - two of us were Java EE guys, another a Objective-C/C++ desktop developer and another having worked on Java applications that integrated with Google Earth.

jQuery and jQuery Mobile are incredibly simple to learn if you have a basic understanding of HTML and JavaScript.  Moving JSON back and forth between a RESTful endpoint is very easy.   This makes prototyping the front-end go incredibly fast.  There are plenty of gotchas or "aha" moments associated with having multiple pages in a single HTML file and dynamically rendered elements that also have their own event listeners that caused some initial bewilderment - especially by a Java EE guy like me, who has mostly lived on "the server side".

In addition, we were working in the same room as the business strategy folks who were filling up the walls with notes, conducting online surveys, debating the pricing model, discussing market dynamics - and discussing these items with outside observers and mentors - there was a constant parade of people coming into the room to chat with us.  

Another interesting aspect of this adventure was using MongoDB, most of us had worked with a RDBMS before, so you make some early mistakes - especially when you have a crazy deadline and you just met your fellow adventurers a few hours before.  While Node.js + Express + Mongo makes it very easy to retrieve data and throw that JSON out to a jQuery client application (using $.getJSON) and it is also easy to receive a POST with a JSON payload to be persisted - it is NOT a RDBMS - do not normalize the data model.  We made that initial mistake - and when we had to write our first report - we realized that there was no simple join to get the data back out again.  Luckily, Chris (strong code-fu) was able to show off his python skills to rip through the data to produce the report we needed.

Remember, the key thing is validation (and learning), we put our application out in front of a live audience.  Taking all the orders for the food truck that specific evening, asking all parties what they thought of the idea.  And of course we had to interview the food truck operator.

This story might terrify you - and it certainly caused me to lose a fair bit of sleep over the weekend - but I have had crazier projects in the past as a consultant.   For those of you who have worked with small businesses or simply been in the consulting game - you get the assignment of "you are now an expert in technology X, show up at the customer site this coming Monday, you get the whole weekend to prepare".  Or better still, the small business owner that has contracted you to build him a new custom application, who does not understand things like what an "alpha" test is and the next thing you are taking millions of dollars in real transactions on a piece of code that you were not able to test and was not completed.  Performing surgery on the live system on the production server - yep, I have done that.

Now, I have purposely been somewhat vague about the overall idea and the details of the architecture.  That is because the Truxie founders will likely wish to pursue this opportunity further.  And while my day job is focused on open source - I consider the code we prototyped to be Truxie's IP, not something I can publish to the world at large.   With that said, I am working on a tutorial that uses jQuery Mobile with a Java EE 6 backend - JAX-RS & JPA to target a RDBMS.  One of the coolest things about a jQuery front-end is that it is completely portable - build the backend in anything you like.

I did manage to make a demo video of the application, navigating the application with my lefthand while filming with right hand - that was a trick all by itself.   Yes, I removed the sound - but I should have left it in, you could have experienced all the discussion and chaos going on around me.

Myself and my JBoss by Red Hat teammates will be publishing some additional tutorials that show off how to use JBoss as your backend and jQuery + Backbone.js + Underscore.js as your frontend over the next few weeks.  Keep an eye on for updates.

And I plan to continue experimenting with Node.js + Express + MongoDB.  But that will have to wait a bit longer, my son wishes to learn more about iOS game development.   So I installed XCode and will start digging into Cocos2D tonight - keeps you young. :-)

Perhaps next year's Triangle Startup Weekend will have a father + son team competing.  Plus, my wife is an amazing marketer - we almost have a complete team - just need the idea person!

Saturday, February 18, 2012

Barnes and Noble FAIL

I know, I rarely blog.  I tweet some but otherwise blogging is a challenge - just too many communication tools to keep up with.  :-)  I only login into Facebook about once a month and into LinkedIn about once every 3 months.  And my email inboxes normally have several hundred unread emails.    Well, enough about my communication challenges.   Why am I writing this blog? Because I used to like Barnes & Noble, I still enjoy walking into the store, browsing the shelves of books, picking up a cup of coffee, etc.  I am the owner of many hundreds of books and I have spent more time in bookstores than most people have spent at movie theaters.  The problem is that years ago I started buying almost everything from Amazon, as a Plus member, shipping is free and insanely fast. Furthermore, I am starting to get most of my books directly to the Kindle app on my Android phone or iPad.  

Well, I tried to make an impulse buy/online purchase from today and the stupid checkout process requires a phone number? Which is fine but when I entered my name/address, I skipped over the phone number field - it was not marked as mandatory.  And then I was stuck - I could not return to change my contact information but I also could not move forward to complete the transaction.   Perhaps I am a dumb-user but I hope B&N is tracking failed transactions.   I tend to be a hurried end-user and I like my shopping carts/checkout process to be nearly brain-dead easy - otherwise I will just go find the product on Amazon.

And did I mention that I really hate, contact us HTML forms? Especially ones presented as modal dialogs? Plus, broken image links, especially in your "Contact Customer Service" section really make you look bad.  After all, I have navigated to this section because I have a question or a concern to raise.     And I wrote this blog because the "Send Email" web form failed to accept my gmail address.

I guess I can still go to B&N to get the coffee and sit down and read my Kindle-based books!