Wednesday Apr 02, 2008

Merb on JRuby 1.1 RC3


This blog provides how you can get started with Merb on JRuby 1.1 RC3.

Merb is another MVC framework (just like Rails) but with a pluggable ORM, JavaScript library and Template language. Rails has built-in support for these using ActiveRecord, Script.aculo.us and ERB templates. Making it pluggable keeps the core very lightweight and still providing support for a particular feature using plugins. Another big advantage of Merb is that unlike Rails it's thread-safe.

There are already third party supports for ActiveRecord, DataMapper and Sequel ORMs. I will hopefully be able to build support for Java Persistence API ORM in Merb. Until then, here is how you install and get started with Merb on JRuby 1.1 RC3.

Install Merb on JRuby as:
Macintosh-187:jruby-1.1RC3 arungupta$ bin/jruby -S gem install merb mongrel
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
Updating metadata for 339 gems from http://gems.rubyforge.org
.........................................................................................................
.........................................................................................................
.........................................................................................................
........................
complete
Successfully installed abstract-1.0.0
Successfully installed erubis-2.5.0
Successfully installed json_pure-1.1.2
Successfully installed rack-0.3.0
Successfully installed hpricot-0.6-java
Successfully installed mime-types-1.15
Successfully installed merb-core-0.9.2
Successfully installed merb-action-args-0.9.2
Successfully installed merb-assets-0.9.2
Successfully installed activesupport-2.0.2
Successfully installed rubigen-1.2.4
Successfully installed merb-gen-0.9.2
Successfully installed merb-haml-0.9.2
Successfully installed merb-builder-0.9.2
Successfully installed mailfactory-1.2.3
Successfully installed merb-mailer-0.9.2
Successfully installed merb-parts-0.9.2
Successfully installed merb-cache-0.9.2
Successfully installed merb-more-0.9.2
Successfully installed merb-0.9.2
Successfully installed gem_plugin-0.2.3
Successfully installed mongrel-1.1.4-java
22 gems installed
Installing ri documentation for json_pure-1.1.2...
Installing ri documentation for rack-0.3.0...
Installing ri documentation for hpricot-0.6-java...
Installing ri documentation for mime-types-1.15...
Installing ri documentation for merb-core-0.9.2...
Installing ri documentation for merb-action-args-0.9.2...
Installing ri documentation for merb-assets-0.9.2...
Installing ri documentation for activesupport-2.0.2...
Installing ri documentation for rubigen-1.2.4...
Installing ri documentation for merb-gen-0.9.2...
Installing ri documentation for merb-haml-0.9.2...
Installing ri documentation for merb-builder-0.9.2...
Installing ri documentation for mailfactory-1.2.3...
Installing ri documentation for merb-mailer-0.9.2...
Installing ri documentation for merb-parts-0.9.2...
Installing ri documentation for merb-cache-0.9.2...
Installing ri documentation for gem_plugin-0.2.3...
Installing ri documentation for mongrel-1.1.4-java...
Installing RDoc documentation for json_pure-1.1.2...
Installing RDoc documentation for rack-0.3.0...
Installing RDoc documentation for hpricot-0.6-java...
Installing RDoc documentation for mime-types-1.15...
Installing RDoc documentation for merb-core-0.9.2...
Installing RDoc documentation for merb-action-args-0.9.2...
Installing RDoc documentation for merb-assets-0.9.2...
Installing RDoc documentation for activesupport-2.0.2...
Installing RDoc documentation for rubigen-1.2.4...
Installing RDoc documentation for merb-gen-0.9.2...
Installing RDoc documentation for merb-haml-0.9.2...
Installing RDoc documentation for merb-builder-0.9.2...
Installing RDoc documentation for mailfactory-1.2.3...
Installing RDoc documentation for merb-mailer-0.9.2...
Installing RDoc documentation for merb-parts-0.9.2...
Installing RDoc documentation for merb-cache-0.9.2...
Installing RDoc documentation for gem_plugin-0.2.3...
Installing RDoc documentation for mongrel-1.1.4-java...
This is so much simpler than couple of weeks ago where all the dependencies had to be explicitly installed. Now create a sample application as:

Macintosh-187:jruby-1.1RC3 arungupta$ cd samples/
Macintosh-187:samples arungupta$ mkdir merb
Macintosh-187:samples arungupta$ cd merb/
Macintosh-187:merb arungupta$ ../../bin/jruby -S merb-gen app hello
RubiGen::Scripts::Generate
      create  app
      create  autotest
      create  config
      create  public
      create  spec
      create  app/controllers
      create  app/helpers
      create  app/views
      create  app/views/exceptions
      create  app/views/layout
      create  config/environments
      create  public/images
      create  public/stylesheets
      create  autotest/discover.rb
      create  autotest/merb.rb
      create  autotest/merb_rspec.rb
      create  config/rack.rb
      create  config/router.rb
      create  config/init.rb
      create  public/merb.fcgi
      create  spec/spec.opts
      create  spec/spec_helper.rb
      create  app/controllers/application.rb
      create  app/controllers/exceptions.rb
      create  app/helpers/global_helpers.rb
      create  app/views/exceptions/internal_server_error.html.erb
      create  app/views/exceptions/not_acceptable.html.erb
      create  app/views/exceptions/not_found.html.erb
      create  app/views/layout/application.html.erb
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/rake.rb
      create  config/environments/test.rb
      create  public/images/merb.jpg
      create  public/stylesheets/master.css
      create  /Rakefile

The top-level directory looks like:

Macintosh-187:hello arungupta$ ls -la
total 8
drwxr-xr-x  8 arungupta  arungupta   272 Apr  1 22:20 .
drwxr-xr-x  3 arungupta  arungupta   102 Apr  1 22:20 ..
-rw-r--r--  1 arungupta  arungupta  3334 Apr  1 22:20 Rakefile
drwxr-xr-x  5 arungupta  arungupta   170 Apr  1 22:20 app
drwxr-xr-x  5 arungupta  arungupta   170 Apr  1 22:20 autotest
drwxr-xr-x  6 arungupta  arungupta   204 Apr  1 22:20 config
drwxr-xr-x  5 arungupta  arungupta   170 Apr  1 22:20 public
drwxr-xr-x  4 arungupta  arungupta   136 Apr  1 22:20 spec

And finally start the app as:

Macintosh-187:hello arungupta$ ../../../bin/jruby -S merb
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
 ~ Loaded DEVELOPMENT Environment...
 ~ Compiling routes...
 ~ Using 'share-nothing' cookie sessions (4kb limit per client)
 ~ Using Mongrel adapter

And the default app is hosted at "http://localhost:4000/" and shown in the browser as:

There is a wealth of information available to get you going after this. Here are couple of things to try:
  • If possible, use WARbler to package Merb app and deploy on GlassFish.
  • Try Java Persistence API as the pluggable ORM.

Technorati: rubyonrails merb jruby ruby jpa glassfish

Wednesday Jan 23, 2008

RESTful representation of "sakila" using GlassFish and NetBeans IDE

"sakila" is the sample database shipped with MySQL (pronounced as my ess-kew-ell). In the context of Sun Microsystems announcing the agreement to acquire MySQL, I'd like to dedicate this entry to show how this sample database can be exposed as a RESTful Web service endpoint and deployed on GlassFish using Jersey Tooling Plugin (0.4.1 with Jersey 0.4) in NetBeans IDE.

Lets get started!

  1. Install MySQL & the sample database "sakila".
    1. Download and Install MySQL Community Server.
    2. Download sakila sample database.
    3. Install the database as described here.
    4. Start MySQL database by giving the command 'mysqld-nt --user root --console' in bin directory on Windows or './bin/mysqld_safe' from MySQL directory on Unix flavors.
  2. Create the Project & Database Connection
    1. In NetBeans IDE, create a new Web project and name it as "sakila". Choose "GlassFish v2" as the "Server:".
    2. In the "Services" tab of NetBeans IDE, expand "Drivers" and add MySQL Connector/J driver if it does not exist already.
    3. Create a new new database connection by right-clicking on "Drivers" and specifying the parameters as shown below:

  3. Create the Persistence Unit
    1. Right-click on the project and select "New", "Entity Classes from Database...". In "Data Source", select "New Data Source..." and specify the values as shown below:

    2. Click on "film" in "Available Tables" and click on "Add >" as shown below:



      Click on "Next >".
    3. Click on "Create Persistence Unit..." and take all the defaults as shown below:



      Click on "Create".
    4. Enter the package name as "sakila" as shown below:



      and click on "Finish".
    5. In the NetBeans project explorer, expand "Configuration Files" and open "persistence.xml". Specify the username and password by replacing <properties/> with the following fragment:

      <properties>
        <property name="toplink.jdbc.user" value="root"/>
        <property name="toplink.jdbc.password" value=""/>
      </properties>


      Make sure to match the username and password to your MySQL installation.
  4. Create RESTful Web service endpoint
    1. In NetBeans IDE, click on "Tools", "Plugins", "Available Plugins", "RESTful Web Services" and then click on "Install". This installs the Jersey Tooling Plugin in the IDE.
    2. Right-click on the project, select "New", "RESTful Web Services from Entity Classes...".
    3. Click on "Add >>", take all other defaults as shown below:



      click on "Next >", take all defaults and then "Finish".
  5. Test RESTful Web Services
    1. Right-click on the project and select "Test RESTful Web Services". The following web page is presented in the browser:

    2. Click on "films" and then on "Test" as shown below:



      Clicking on "Test" button or the URL "http://localhost:8080/sakila/resources/films/" shows the RESTful representation of the "Film" table. The default representation shows 10 records from the table where each entry returns the "id" of the film and a reference to the detailed entry.

      You can view more entries (say 40) by giving the URL "http://localhost:8080/sakila/resources/films/?max=40". Additional fields from the table can be displayed by adding getter methods to "converter.FilmRefConverter" class such as:

      @XmlElement
      public String getTitle() {
        return entity.getTitle();
      }


      to return the film title in addition to the fields already returned. The different columns in the table can be viewed by going to the "Services" tab, expanding the sakila database connection created earlier as shown below:



      The modified output (with film title included) looks as shown below:

Here are few more ideas for you to explore:

  • Create RESTful representations of other tables using the steps described above.
  • Display the data from different tables in a jMaki-wrapped Yahoo or Dojo data table as explained in TOTD #10.
  • Display the data retrieved from the database in a JSP page as described in Hello JPA World.
  • Create a CRUD application using jMaki Data Table as described in TOTD #15 or Screencast #Web10.

A JRuby-on-Rails application using MySQL is explained here. TOTD #9 explains how JDBC connection pooling in GlassFish can be used for a JRuby-on-Rails application using MySQL.

The key message here is MySQL can be very easily used with GlassFish and NetBeans IDE makes it possible! Once MySQL becomes part of Sun, this integration is going to be much more seamless for the betterment of community.

All the entries on this blog using MySQL can be found here. And last but not the least, Welcome aboard MySQL!

A NetBeans project with all the source code can be downloaded from here. You will still need to setup the database connection and need to make sure the correct version of Jersey plug-in as well :)

Technorati: glassfish netbeans jersey mysql sakila jpa jmaki rubyonrails

Monday Dec 31, 2007

Screencast #Web11: Travel Map - Another Real-life app using jMaki & Jersey

In my role of Technology Evangelist, I get the opportunity to meet a lot of community (folks like you :) all around the world. In the year 2007, I represented GlassFish (and related technologies - Metro, jMaki and Jersey) at multiple conferences. This blog introduces a  new real-life application that plots all the places I visited this year on a jMaki-wrapped Google Map widget. Clicking on the marker shows more information about the event such as dates and the blog entry covering the event.

Play the video below to see how the application looks like.

Here is the architecture of this application:

travel map architecture

It consists of a server-side and a client-side applications - developed as NetBeans projects.

  1. Server-side project - A RESTful Web service endpoint that provides resource represenations for all the events attended and associated meta information such as date and blog URLs. This endpoint is created using Jersey.
  2. Client-side project - A jMaki-enabled Web application that consumes the representations generated by the RESTful Web service and plots the information on a jMaki-wrapped Google Map widget.

Both the server-side and client-side are deployed on GlassFish.

This is only a sample application so optimizations are certainly possible and corner cases (such as no blog entry for a particular visit) are not accounted for. But the application still demonstrates the concept. The fully built application looks like as shown below:

Arun's Travel Map 2007

My first presentation in this role was Sun Tech Days Atlanta (highlighted in the image). This application generates an interactive Google Map so feel free to zoom in/out and click 

And one last thing before we build the application. Here is the list of technologies and associated concepts used to build this application:

  1. Jersey
    1. Shows an example of how RESTful Web services can be easily generated from JPA Entity Classes.
    2. Shows how all the resource representations (instead of reference to individual resources) can be returned by a Jersey endpoint.
  2. jMaki
    1. Shows how to consume XML data from an external service (RESTful Web service endpoint) in this case.
    2. Shows how the underlying data model of a widget (Google Map in this case) can be accessed and manipulated.
  3. GlassFish
    1. All the applications are deployed on GlassFish - implicit in the development/deplyment process through seamless integration with NetBeans.
  4. NetBeans 6
    1. Used for generation of RESTful Web services from JPA Entity Classes.
    2. Used for generating/deploying jMaki projects and drag-and-drop of jMaki-wrapped widgets.
  5. JavaScript Closures - to persist the state for asynchronous callback functions
  6. JavaScript DOM processing - to process the XML data received from Jersey endpoint.
  7. Google Maps API
    1. Generate meaningful markers on each location
    2. Populate Google Map from a RESTful Web service endpoint
  8. Java Persistence API - to retrieve data from the database.

And finally, lets build this application. Lets build the RESTful Web service endpoint project first.

  1. Create and Populate the Database
    1. In the NetBeans IDE, go to Services tab, and connect to the database with URL "jdbc:derby://localhost:1527/sample [app on APP]" (right-click and select "Connect...").
    2. Right-click on this database and select "Execute Command..." and create a table by giving the following command:

      create table EVENTS (id int GENERATED ALWAYS AS IDENTITY,
                          event_name varchar(255),
                          dates varchar(20),
                          venue varchar(255),
                          blogs varchar(2056),
                          PRIMARY KEY (id))


      Notice, the "id" column is marked as IDENTITY that instructs the database to auto generate the values for this column and increment by 1 (default) for each row. This column is also marked as the primary key.
    3. Again right-click on the database and select "Execute Command..." to add data to the table by giving the following command:

      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('SunTech Days - Atlanta', 'Jan 16 - Jan 17', 'Cobb Galleria Center, Two Galleria Parkway, Atlanta, Georgia, 30339', 'http://blogs.sun.com/arungupta/entry/wsit_and_web_2_0');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('jMaki Day', ' Feb 23', '4150 Network Circle Santa Clara, CA 95054', 'http://blogs.sun.com/arungupta/entry/sun_internal_jmaki_day_review');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Ajax World - New York', 'Mar 19 - Mar 21', 'The Roosevelt Hotel, 45 E 45th St, New York, NY 10017', 'http://blogs.sun.com/arungupta/entry/sun_ajax_world');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('The Server Side Java Symposium - Las Vegas', 'Mar 22', '3355 Las Vegas Blvd. South Las Vegas, NV 89109', 'http://blogs.sun.com/arungupta/entry/sun_the_server_side_java, http://blogs.sun.com/arungupta/entry/tango_at_venetian_las_vegas');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('JavaOne - San Francisco', 'May 7 - May 11', 'Moscone Center, 747 Howard Street, San Francisco, CA 94103', 'http://blogs.sun.com/arungupta/entry/slides_for_ts_4865, http://blogs.sun.com/arungupta/entry/javaone_2007_day_1_finished, http://blogs.sun.com/arungupta/entry/javaone_2007_day_1, http://blogs.sun.com/arungupta/entry/javascript_everywhere_javaone_2007_demo, http://blogs.sun.com/arungupta/entry/excel_using_wsit_javaone_2007, http://blogs.sun.com/arungupta/entry/ts_4865_takes_two_to, http://blogs.sun.com/arungupta/entry/communityone_glassfish_day_report, http://blogs.sun.com/arungupta/entry/javaone_2007_backstage, http://blogs.sun.com/arungupta/entry/javaone_2007_is_almost_here, http://blogs.sun.com/arungupta/entry/my_javaone_2007_picks');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Rails Conf - Portland', 'May 17 - May 20', '777 NE MLK, Jr. Blvd. Portland, OR 97232', 'http://blogs.sun.com/arungupta/entry/tim_bray_s_keynote_session, http://blogs.sun.com/arungupta/entry/sun_rails_conf_2007_keep, http://blogs.sun.com/arungupta/entry/getting_started_with_jruby_tutorial, http://blogs.sun.com/arungupta/entry/jmaki_netbeans_and_glassfish_in');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Google Developer Day - San Jose', 'May 31', '150 W San Carlos St San Jose, CA 95113', 'http://blogs.sun.com/arungupta/entry/google_developer_day_report');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Mashup Camp - Mountain View', 'Jul 18 - Jul 19', 'Computer History Museum, 1401 N Shoreline Blvd., Mountain View, CA 94043', 'http://blogs.sun.com/arungupta/entry/jmaki_at_mashup_camp_report, http://blogs.sun.com/arungupta/entry/jmaki_mashup_camp');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('OSCON - Portland', 'Jul 23 - Jul 27', '777 NE MLK, Jr. Blvd. Portland, OR 97232', 'http://blogs.sun.com/arungupta/entry/jmaki_oscon');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('JRuby Hack Day - San Francisco', 'Aug 8', '1201 8th St, San Francisco, CA 94107', 'http://blogs.sun.com/arungupta/entry/jruby_on_rails_hackday_report, http://blogs.sun.com/arungupta/entry/learn_jruby_on_rails_free');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Rich Web Experience - San Jose', 'Sep 6 - Sep 8', '170 S Market St, San Jose, CA 95113', 'http://blogs.sun.com/arungupta/entry/the_rich_web_experience_2007, http://blogs.sun.com/arungupta/entry/jmaki_javafx_the_rich_web');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Rails Conf Europe - Berlin', 'Sep 17 - Sep 19', 'Maritim Pro Arte, Friedrichstrasse 151, 10117 Berlin', 'http://blogs.sun.com/arungupta/entry/rails_conf_europe_2007_day2, http://blogs.sun.com/arungupta/entry/rails_conf_europe_2007_day1, http://blogs.sun.com/arungupta/entry/rails_conf_europe_2007_day, http://blogs.sun.com/arungupta/entry/jmaki_netbeans_and_glassfish_in1');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Sun Tech Days - Rome', 'Sep 24 - Sep 25', 'Meliá Roma Aurelia Antica, Vía Aldobrandeschi, 223  Rome ITALY  00163', 'http://blogs.sun.com/arungupta/entry/netbeans_day_rome_2007, http://blogs.sun.com/arungupta/entry/travel_tips_to_rome, http://blogs.sun.com/arungupta/entry/glassfish_metro_jersey_and_jmaki');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Sun Tech Days - Milan', 'Sep 26 - Sep 28', 'ATA Hotel Quark - Via Lampedusa 11/a 20141 Milano, Italia', 'http://blogs.sun.com/arungupta/entry/glassfish_day_milan_2007');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Mid West Java Tech Days - Minneapolis', 'Oct 16', 'University of St Thomas, MPL 201, 1000 LaSalle Avenue, Minneapolis, MN 55403-2005', 'http://blogs.sun.com/arungupta/entry/mid_west_java_tech_days, http://blogs.sun.com/arungupta/entry/metro_and_jmaki_in_minneapolis');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Mid West Java Tech Days - Chicago', 'Oct 18', 'Donald E Stephens Convention Center, 9301, W Bryn Mawr Ave, Rosemont IL 60018', 'http://blogs.sun.com/arungupta/entry/mid_west_java_tech_days1, http://blogs.sun.com/arungupta/entry/crowne_plaza_chicago_o_hare, http://blogs.sun.com/arungupta/entry/metro_and_jmaki_in_minneapolis');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Silicon Valley Code Camp - Los Altos', 'Oct 27', 'Foothill College, Los Altos, CA', 'http://blogs.sun.com/arungupta/entry/silicon_valley_code_camp_trip, http://blogs.sun.com/arungupta/entry/metro_jmaki_silicon_valley_code');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Sun Tech Days - Beijing', 'Nov 1 - Nov 3', 'Beijing International Convention Center, No.8 Beichendong Road Chaoyang District, Beijing', 'http://blogs.sun.com/arungupta/entry/glassfish_day_beijing_2007_by, http://blogs.sun.com/arungupta/entry/wangfujing_street_authentic_china_in, http://blogs.sun.com/arungupta/entry/sun_tech_days_beijing_talent, http://blogs.sun.com/arungupta/entry/sun_tech_days_beijing_day, http://blogs.sun.com/arungupta/entry/travel_tips_to_beijing, http://blogs.sun.com/arungupta/entry/glassfish_day_beijing');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Toronto', 'Nov 21', 'Toronto City Center', 'http://blogs.sun.com/arungupta/entry/metro_jmaki_jruby_glassfish_q');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Montreal', 'Nov 21', 'Montreal City Center', 'http://blogs.sun.com/arungupta/entry/metro_jmaki_jruby_glassfish_q');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('GlassFish - Delhi University', 'Dec 3', 'New Delhi', 'http://blogs.sun.com/arungupta/entry/glassfish_delhi_university');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('FOSS.IN - Bangalore', 'Dec 4', 'India Institute of Science, Bangalore', 'http://blogs.sun.com/arungupta/entry/packaging_java_apps_for_ubuntu, http://blogs.sun.com/arungupta/entry/foss_in_schedules_now_available, http://blogs.sun.com/arungupta/entry/glassfish_foss_in_2007');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Bangalore', 'Dec 4', 'Bangalore', 'http://blogs.sun.com/arungupta/entry/glassfish_bangalore_chennai_and_pune');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Chennai', 'Dec 5', 'Chennai', 'http://blogs.sun.com/arungupta/entry/glassfish_bangalore_chennai_and_pune');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - Pune', 'Dec 6', 'Pune', 'http://blogs.sun.com/arungupta/entry/glassfish_bangalore_chennai_and_pune');
      INSERT INTO EVENTS (event_name, dates, venue, blogs) VALUES('Partner Preso - San Francisco', 'Dec 17', 'San Francisco', 'http://blogs.sun.com/arungupta/');

      These SQL statements populate the database with details about my visits in 2007. If you'd like to develop a similar application highlighting your visits then you'll need to modify the VALUES clause to match accordingly.
  2. Create and Configure RESTful Web service endpoint
    1. Create a Persistence Unit as described in "Generating Entity Classes from Database" section in Getting Started with RESTful Web Services. Lets say the project name is "WebApplication3", package name is "events" and the table name to generate Entity classes is EVENTS. Take everything else as the defaults.
    2. Generate a RESTful Web service as described in "Generating RESTful Web Services from Entity Classes" section in Getting Started with RESTful Web Services.
      1. Add a new class EventsList in the events package as:

        @javax.xml.bind.annotation.XmlRootElement
        public class EventsList {
          @javax.xml.bind.annotation.XmlElement
          protected java.util.List<Events> events;

          public EventsList() {
            if (events == null)
              events = new java.util.ArrayList<Events>();
          }

          public void add(Events name) {
            events.add(name);
          }

          public java.util.List<Events> getValue() {
            return events;
          }
        }
      2. In service.EventsResource, change the method associated with GET to:

        public EventsList get() {
                EventsList eventsList = new EventsList();
                List<Events> list  = PersistenceService.getInstance().createQuery("SELECT e FROM Events e").getResultList();
                for (Events e : list) {
                    eventsList.add(e);
                }
                return eventsList;
        }

        This will ensure that all the resource representations are returned instead of a reference to the resource. Make sure to fix the imports.
That's it, our server-side project is now ready. "http://localhost:8080/WebApplication3/resources/events" now return a complete RESTful representation of all the rows from the database table EVENTS.

Lets build the client-side application next. Make sure jMaki plug-in in NetBeans IDE is already installed.
  1. In the NetBeans IDE, create a new Web project, enable "Ajax Framework" and choose the "Standard" layout for "index.jsp".  Lets say the project name is "WebApplication4".
  2. Drag-and-drop a jMaki-wrapped Google Map widget in the 'Main Content Area' and jMaki-wrapped Yahoo Button in the 'Sidebar Content Here'.
  3. Customise the widgets
    1. Add id="mymap" attribute to the Google Map widget. The updated widget looks like as shown below:

      <a:widget  name="google.map" id="mymap"
               args="{ centerLat : 37.4041960114344,
                       centerLon : -122.008194923401 }" />

      id="mymap"
      will allow the Map widget to be accessed by name later.
    2. Add args="{label:'Plot Events'}" attribute to thes Yahoo button widget. The updated widget looks like as shown below:

      <a:widget name="yahoo.button" args="{label:'Plot Events'}"/>
  4. In glue,js, add the following code to \*onClick subscribe method:

    var url = jmaki.xhp + "?id=events";
    var _map = jmaki.getWidget("mymap").map;
    _map.setZoom(2);
    _map.clearOverlays();
    _map.enableInfoWindow();
       
    jmaki.doAjax({method: "GET",
        url: url,
        callback: function(_req) {          
            var xmlobject = (new DOMParser()).parseFromString(_req.responseText, "text/xml");
            var root = xmlobject.getElementsByTagName('eventsList')[0];
            var events = root.getElementsByTagName('events');
            for (var i = 0 ; i < events.length ; i++) {
                var event = events[i];
                var eventName = event.getElementsByTagName('eventName')[0].firstChild.nodeValue;
                var venue = event.getElementsByTagName('venue')[0].firstChild.nodeValue;
                var blogs = event.getElementsByTagName('blogs')[0].firstChild.nodeValue;
                var dates = event.getElementsByTagName('dates')[0].firstChild.nodeValue;
               
    var id = event.getElementsByTagName('id')[0].firstChild.nodeValue;
                   
                var encodedLocation = encodeURIComponent("location=" + venue);
                var url = jmaki.xhp + "?id=yahoogeocoder&urlparams=" + encodedLocation;
                jmaki.myHandler(url, eventName, blogs, dates, id, _map);
            }
        }
    });
  5. Add the following functions above the \*onClick subscribe method:

    // "Function closure" used from http://econym.googlepages.com/basic1.htm
    // Creates local copy of "marker" and "html" variables to be preserved for later use
    function createMarker(point,html) {
        var marker = new GMarker(point);
        GEvent.addListener(marker, "click", function() {
            marker.openInfoWindowHtml(html);
        });
        return marker;
    };

    // Function closure that preserves "eventName", "blogs", "dates and "id"
    // Gets the latitude/longitude from Yahoo Geocoding service and plots them on the map
    // Also creates meaningful markers
    jmaki.myHandler = function(_url, eventName, blogs, dates, id, _map) {
        jmaki.doAjax({url: _url,
            callback : function(req) {
                if (req.responseText.length > 0) {
                    jmaki.log("name: " + eventName);
                    var response = eval("(" + req.responseText + ")");
                    var coordinates = response.coordinates;
                    jmaki.publish("/jmaki/plotmap", coordinates);
                    jmaki.log("plotting " + eventName);
                    var latlng = new GLatLng(coordinates[0].latitude, coordinates[0].longitude);
                   
                    var blogHtml = "";
                    b = blogs.split(', ');
                    for (i=0; i<b.length; i++) {
                        blogHtml += '<a href="' + b[i] + '">' + (i+1) + '</a>';
                        if (i<b.length-1)
                            blogHtml += ", ";
                    }
                   
                    var txt = '<table>' +
                    '<tr><td>#' + id + ": " + eventName + '</td></tr>' +
                    '<tr>Dates: ' + dates + ', 2007</td></tr>' +
                    '<tr><td>Blogs: ' + blogHtml + '</td></tr>' +
                    '</table>';
                   
                    var marker = createMarker(latlng, txt);
                    _map.addOverlay(marker);
                    marker.openInfoWindowHtml(txt);
                } else {
                    jmaki.log("Failed to get coordinates for " + location );
                }
            }
        });   
    };
  6. Add the following entry in Web Pages, resources, xhp.json:

    ,
    {"id": "events",
     "url":"http://localhost:8080/WebApplication3/resources/events/"
    }


    assuming WebApplication3 is the project where RESTful Web service endpoint is hosted.
That completes our client-side web application as well. Now, either hit F6 (default key to Run the NetBeans project) and this will show http://localhost:8080/WebApplication4/index.jsp in the configured browser. Once you click on "Plot Events" button, all the markers on the Google Map are plotted.
Future Improvements
  1. If Jersey can return all the resource representations directly, then the workaround used above may not be required.
  2. Use e4x after <script type="text/javascript"> in index.jsp can be generated as <script type="text/javascript; e4x=1">.
  3. Build the client-side application using Rails once issue #309 & #310 are resolved.
    1. Once deployed as Rails application on WEBrick, create a WAR file and deploy on GlassFish.
    2. Try this application using GlassFish v3 gem.
  4. Embed Google Map in the blog entry.
An alternate title of this blog entry could've been "How I spent my winter break ?". But in order to keep the title inline with rest of other entries (keeping it simple and reflecting the content of the entry) I decided to use the existing title ;-)

Technorati: screencast conf jmaki jersey netbeans glassfish jpa javascript googlemaps restful web2.0 jmakimashups

Wednesday Nov 07, 2007

Screencast #Web10: CRUD using jMaki and JPA

This screencast shows how to create a simple jMaki application, using NetBeans IDE, that performs some of the CRUD operations on a Data Table widget. It uses Java Persistence API (JPA) to connect to the database and the application is deployed on GlassFish. The rest of the CRUD operations can be easily built using the same methodology.

The steps followed in this screencast are also described in detail.

Enjoy it here!

Technorati: screencast jmaki netbeans glassfish jpa database crud

Tuesday Oct 30, 2007

TOTD #15: Delete/Update Row from Database using jMaki Data Table

A Previous Entry explained how a Data Table widget can be populated from a database using Java Persistence API (JPA). This TOTD extends that entry and explains how a selected row from the Data Table can be deleted from the database. This entry is created based upon a requirement from Dave Briccetti at Silicon Valley Code Camp 2007 last weekend.

The first part of the entry is also a re-write of using NetBeans 6 and the latest jMaki NetBeans plugin.

  1. Create the Web application project
    1. In NetBeans 6 IDE, create a new 'Web Application' project and name it as 'jmaki-database'.
    2. Choose GlassFish V2 as the server as shown below:

    3. Click on 'Next' button, add 'jMaki Ajax Framework' and choose 'Standard' layout as shown below:



      and click on 'Finish' button.
  2. Configure the Database
    1. In NetBeans IDE, 'Runtime' tab, expand Databases, connect to the default database (with the URL 'jdbc:derby://localhost:1527/sample [app on APP]'). Specify the username 'app' and password 'app'.
    2. Right-click again on the URL and select 'Execute Command...' and issue the command:

      create table BOOKS (title varchar(255),
                          author varchar(255),
                          isbn varchar(255),
                          description varchar(255),
                          PRIMARY KEY (isbn))


      This will create the database table.
    3. Add data to the newly created table using the following command:

      INSERT INTO BOOKS VALUES('Galloway Book of Running', 'Jeff Galloway', 'ABC001', 'The best book on running');
      INSERT INTO BOOKS VALUES('The Complete Book of Running', 'James Fixx', 'ABC002', 'Oldest book of running');
      INSERT INTO BOOKS VALUES('The Runners Handbook', 'Bob Glover', 'ABC003', 'Bestselling Guide for Beginning and Intermediate Runners');
      INSERT INTO BOOKS VALUES('Daniel Running Formula', 'Jack Tupper Daniels', 'ABC004', 'Proven programs 800m to Marathon');
      INSERT INTO BOOKS VALUES('Chi Running', 'Danny Drever', 'ABC005', 'Revolutionary approach to effortless, injury-free running');
      INSERT INTO BOOKS VALUES('Running for Mortals', 'John Bingham', 'ABC006', 'A common sense plan for changing your life through running');
      INSERT INTO BOOKS VALUES('Marathoning for Mortals', 'John Bingham', 'ABC007', 'Regular person guide to marathon');
      INSERT INTO BOOKS VALUES('Marathon', 'Hal Higdon', 'ABC008', 'The Ultimate Training Guide');


      This will add 8 rows to the table. You can enter additional rows if you like.
  3. Create the JPA Entity class that maps to the database
    1. In the projects window, select the project 'jmaki-database', right-click and select 'New' and choose 'Entity Classes From Database...'.
    2. Select 'jdbc/sample' as 'Data Source'.
    3. Select 'BOOKS' in 'Available Tables' and click on 'Add' and enter the values as shown below:



      and click on 'Next'.
    4. Specify the package name as 'server' as shown below:

    5. Click on 'Create Persistence Unit...' to create the persistence unit and enter the values as shown below:


      and click on 'Create'.

    and click on 'Finish'.

  4. Add the following named query to the generated JPA class:

    @NamedQuery(name = "Books.findAll", query = "SELECT b FROM Books b")
  5. Configure Persistence Unit
    1. In your project, expand 'Configuration Files' and open 'persistence.xml'.
    2. Click on 'Add Class' button and  choose 'server.Books' class and click 'OK'. This will ensure that the generated entity class is explicitly recognized by the EntityManagerFactory.
  6. In your project, right-click on 'Web Pages', select 'New' and then 'JSP...'. Give the name as 'data' as shown:



    and then click on 'Finish'.
  7. Replace the entire content of template 'data.jsp' with the following:

    <%@ page import="java.util.\*" %>
    <%@ page import="server.Books" %>
    <%@ page import="javax.persistence.\*" %>

    <%
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("jmaki-databasePU");
      EntityManager em = emf.createEntityManager();

      List<Books> list = em.createNamedQuery("Books.findAll").getResultList();

      out.println("{columns : [" +
        "{ label : 'Title', id : 'title'}," +
        "{ label :'Author', id : 'author'}," +
        "{ label :'ISBN', id : 'isbn'}," +
        "{ label :'Description', id : 'description'}" +
        "],");

        out.println("rows: [");
        for (int i=0; i<list.size(); i++) {
          Books b = list.get(i);
          out.print("{ id: '" + b.getIsbn() + "', " +
            "title: '" + b.getTitle() + "'," +
            "author: '" + b.getAuthor() + "'," +
            "isbn: '" + b.getIsbn() + "'," +
            "description: '" + b.getDescription() + "'}");
          if (i < list.size()-1)
            out.println(",");
          else
            out.println();
        }
        out.println("] }");
      %>
  8. Add and Configure a jMaki Data Table widget
    1. In the generated 'index.jsp', drag-and-drop a 'Yahoo Data Table' widget from the jMaki Palette in the 'Main Content Area'.
    2. Change the generated code fragment from:

      <a:widget name="yahoo.dataTable"
          value="{columns :
                 [
                     { label : 'Title', id : 'title'},
                     { label :'Author', id : 'author'},
                     { label : 'ISBN', id : 'isbn'},
                     { label : 'Description', id : 'description'}
                 ],
                  rows :
                 [
                     { title : 'Book Title 1', author : 'Author 1', isbn: '4412', description : 'A Some long description'},
                     { id : 'bar', title : 'Book Title 2', author : 'Author 2', isbn : '4412', description : 'A Some long description'}
                 ]
                 }" />


      to

      <a:widget name="yahoo.dataTable" service="data.jsp" />

      The 'service' attribute tells jMaki runtime to retrieve the data for DataTable widget from 'data.jsp' instead of the using static data.
    3. Click on the Green button in NetBeans IDE to run the project or default keyboard shortcut (F6). And your browser shows the application deployed as:



      This jMaki-wrapped Yahoo Table Table widget is pulling data from JavaDB.
  9. Now update the project to enable deletion of rows from database based upon row selection. Expand 'Source Packages', 'server', edit 'Books.java' and add the following NamedQuery:

    @NamedQuery(name = "Books.deleteByIsbn", query = "DELETE FROM Books b WHERE b.isbn = :isbn")
  10. In your project, right-click on 'Web Pages', select 'New' and then 'JSP...'. Give the name as shown:



    and then click on 'Finish'.
  11. Replace the entire content of template 'delete.jsp' with the following:

    <%@ page import="javax.persistence.\*" %>

    <%
      String isbn = request.getParameter("isbn");
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("jmaki-databasePU");
      EntityManager em = emf.createEntityManager();

      em.getTransaction().begin();
      em.createNamedQuery("Books.deleteByIsbn").
        setParameter("isbn", isbn).
        executeUpdate();
      em.getTransaction().commit();
    %>
  12. Expand 'Web pages' and edit 'glue.js' to add the following fragment in '\*onSelect' subscribe method:

    jmaki.doAjax({method: "POST",
      url: "delete.jsp?isbn=" + encodeURIComponent(args.value.isbn),
      callback: function(_req) {
        jmaki.publish('/jmaki/table/removeRow', { targetId: args.value.isbn });
      }
    });
  13. Change the generated code fragment in 'index.jsp' as:

    <a:widget name="yahoo.dataTable" service="data.jsp" subscribe="/jmaki/table"/>

That's it! Now clicking on any row of the table will delete that particular row from the database and also from the table. If jMaki Debugger Console is enabled, then the messages are shown as below:

Using the similar steps described in bullet #9-13, a row can be updated in the database.

Please leave suggestions on other TOTD that you'd like to see. A complete archive is available here.

Technorati: totd jmaki glassfish netbeans jpa database

Monday Sep 10, 2007

Screencast #Web7: Creating Mashups with jMaki - A real-life RIA using jMaki (Sun Tech Days Event Map)

Sun Microsystems kicks off 2007-2008 Tech Days and marks the 10th anniversary of the developer event. This blog celebrates the decade by announcing "Sun Tech Days Event Map" - a real-life RIA created using jMaki. It allows you to choose the date on a web page using a rich calendar widget, shows the city where Sun Tech Days event is happening during that month and then shows that city location in a map. Play the video below to see how the application looks like:

This application shows several concepts of jMaki:

  1. Widgets from different toolkits (Dojo Combobox, Yahoo Calendar and Google Map in this case) co-located on the same page
  2. Multiple widgets from different toolkits communicate with each other using the publish/subscribe mechanism & Actions (and here)
  3. Populate widgets by accessing services from the backend
  4. Using Java Persistence API to return the data understood by jMaki widgets

This application is built using NetBeans IDE 5.5.1, jMaki 0.9.7.3 and deployed on GlassFish RC4. Let's get started.

  1. In the NetBeans IDE, create a new project
    1. Right-click in Projects window, select "Web" in "Categories" and "Web application" in "Projects". Click on "Next >".
    2. Enter the project name as "SunTechDays" and choose GlassFish as the "Server" and take all other values default. Click on "Next >".
    3. Select "jMaki Ajax Framework" and choose "Two Fixed Right Sidebars" layout.
    4. Click on "Finish".
  2. Add the following widgets in the generated "index.jsp"
    1. Replace "Top Right Column" by dragging-and-dropping "Dojo Combobox".
    2. Replace "Right Column" with "Yahoo Calendar".
    3. Replace "Left Column" with a "Google Map".
  3. Create and populate the database
    1. In the NetBeans IDE, go to "Runtime" tab, expand "Databases", right-select the node with the value "jdbc:derby://localhost:1527/sample" and select "Connect".
    2. Enter both username and password as "app".
    3. Right-select the database and select "Execute Command...". Create the table using the following SQL

      create table TECHDAYS_SCHEDULE (startDate date,
                                      endDate date,
                                      location varchar(255),
                                      PRIMARY KEY (startDate, endDate))
    4. Populate the database for Sun Tech Days 2007-2008 schedule using the following SQL

      INSERT INTO TECHDAYS_SCHEDULE VALUES ('9/11/2007', '9/12/2007', 'Boston, United States');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('9/24/2007', '9/25/2007', 'Rome, Italy');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('9/26/2007', '9/28/2007', 'Milan, Italy');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('10/19/2007', '10/19/2007', 'Taipei, Taiwan');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('10/23/2007', '10/25/2007', 'Shanghai, China');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('11/1/2007', '11/3/2007', 'Beijing, China');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('11/6/2007', '11/8/2007', 'Tokyo, Japan');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('12/3/2007', '12/5/2007', 'Frankfurt, Germany');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('1/9/2008', '1/10/2008', 'Atlanta, United States');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('2/27/2008', '2/29/2008', 'Bangalore, India');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('3/4/2008', '3/6/2008', 'Sydney, Australia');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('3/11/2008', '3/13/2008', 'Johannesburg, South Africa');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('4/1/2008', '4/1/2008', 'St Petersburg, Russia');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('4/1/2008', '5/1/2008', 'Manila, Philippines');
      INSERT INTO TECHDAYS_SCHEDULE VALUES ('5/21/2008', '5/23/2008', 'Mexico City, Mexico');
  4. Create a Persistence Unit
    1. In the NetBeans IDE, right-select the project, click on "New", "Entity Classes from Database...".
    2. In the "New Entity Classes from Database" dialog window, choose "Data Source" and select "jdbc/sample" value.
    3. All the available tables are shown in "Available Tables". Select "TECHDAYS_SCHEDULE" and click on "Add >".
    4. Click on "Next >".
    5. Enter the package name as "suntech".
    6. Click on "Create Persistence Unit...".
    7. In "Create Persistence Unit..." dialog window take all the default values and click on "Create".
    8. Click on "Finish".
    9. Expand "Configuration Files", open "persistence.xml", click on "Add Class", click "Cancel", click on "Add Class" again and now select "suntech.TechdaysSchedule" and click on "OK".
    10. Expand "Source Packages", "suntech" node and open "TechdaysSchedule.java".
    11. Add the following NamedQuery to the class. Copy/paste the fragment as the first line in "@NamedQueries" annotation.

      @NamedQuery(name = "TechdaysSchedule.findByMonth", query = "SELECT t FROM TechdaysSchedule t WHERE t.techdaysSchedulePK.startdate >= :firstdate and t.techdaysSchedulePK.enddate <= :lastdate"),
  5. Configure the widgets
    1. Expand "Web Pages", open "glue.js" and add the following code at the end of the file:

      jmaki.subscribe("/yahoo/calendar/onSelect", function(args) {
        var newDate;
        if (typeof args.value == "undefined") {
          var tempDate = new Date();
          newDate = (tempDate.getMonth()+1) + "/" + tempDate.getDate() + "/" + tempDate.getFullYear();
        } else {
          var str = "" + args.value;
          newDate = str.split(" ")[1] + "/" + str.split(" ")[2] + "/" + str.split(" ")[3];
        }

        jmaki.doAjax({method: "POST",
            url: "data.jsp?date=" + encodeURIComponent(newDate),
            callback: function(_req) {
              var tmp = _req.responseText;
              var obj = eval("(" + tmp + ")");
              jmaki.log("tmp "+ obj);
              jmaki.publish('/dojo/combobox/setValues', obj);
              // handle any errors
            }
        });
      });

      jmaki.subscribe("/dojo/combobox/onSelect", function(item) {
        var location = item.value.split(',')[0] + ", " + item.value.split(',')[1];
        var start = item.value.indexOf('(');
        var stop = item.value.lastIndexOf(')');
        var encodedLocation = encodeURIComponent("location=" + location);
        // jmaki.xhp is provided as part of jmaki and maps to the XMLHttpProxy
        var url = jmaki.xhp + "?id=yahoogeocoder&urlparams=" + encodedLocation;
        jmaki.doAjax({url: url, callback : function(req) {
          if (req.responseText.length > 0) {
            // convert the response to an object
            var response = eval("(" + req.responseText + ")");
            var coordinates = response.coordinates;
            v = {results:coordinates};
            jmaki.publish("/jmaki/plotmap", coordinates);
          } else {
            jmaki.log("Failed to get coordinates for " + location );
          }
        }
        });
      });
  6. Configure widgets on the page
    1. Change the Dojo Combobox generated fragment with

      <a:widget name="dojo.combobox" service="data.jsp"/>
    2. Right-select the project, select "New" and then "JSP..." using the name "data". Notice, the IDE automatically picks up the file extension.
    3. Replace the generated template code with the following. This code reads a parameter, parses it into a Date, queries the database using the PersistenceUnit created above and return the result in JSON format.

      <%@ page import="java.util.\*" %>
      <%@ page import="suntech.\*" %>
      <%@ page import="javax.persistence.\*" %>
      <%@ page import="java.text.SimpleDateFormat" %>

      <%
        String dateParam = request.getParameter("date");
        Date date = null;
        if (dateParam == null || "".equals(dateParam))
          date = Calendar.getInstance().getTime();
        else {
          SimpleDateFormat sdf = new SimpleDateFormat("MMM/dd/yyyy");
          date = sdf.parse(dateParam);
        }
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("SunTechDaysPU");
        EntityManager em = emf.createEntityManager();

        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        cal.set(Calendar.DATE, 1);
        Date firstDateOfMonth = cal.getTime();

        cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        Date lastDateOfMonth = cal.getTime();

        List<TechdaysSchedule> list = em.createNamedQuery("TechdaysSchedule.findByMonth").
              setParameter("firstdate", firstDateOfMonth).
              setParameter("lastdate", lastDateOfMonth).
              getResultList();
       
        out.println("[");

        boolean first = true;
        int count = 0;
        SimpleDateFormat sdf = new SimpleDateFormat("MMM d, yyyy");
        for (TechdaysSchedule t : list) {
          StringBuffer buf = new StringBuffer();

          buf.append(sdf.format(t.getTechdaysSchedulePK().getStartdate()));
          buf.append(" - ");
          buf.append(sdf.format(t.getTechdaysSchedulePK().getEnddate()));

          String city = t.getLocation() + ", (" + buf.toString() + ")";
          out.println("{");
          out.println("name: \\"" + t.getLocation() + "\\", ");
          out.println("label: \\"" + city + "\\", ");
          out.println("value: \\"" + city + "\\"");
          if (first) {
            out.println(", selected: " + true);
            first = false;
          }
          out.println("}");
          if (count++ < list.size()-1)
            out.println(",");
        }

        out.println("]");
      %>

And that's it!

Carla and I conceived this application together and Greg and  helped with gluing the widgets.

Here are some potential fun improvements:

  1. Instead of randomly selecting a date and then using it's month for populating the combo box, tap the event published (if any) for changing month in the calendar widget.
  2. The database structure can be manipulated to include hotel information and provide more meaningful information.
  3. The dates may be shown in the bubble displayed in the map.
  4. Restaurant recommendations may be pulled from another service and shown for the event attendees.

Technorati: jmaki netbeans glassfish suntechdays jpa screencast web2.0

Monday Aug 27, 2007

Dynamic Data in jMaki Widgets Using JPA - Updated for formal data models

Doris pointed out that one of my earlier post is not working any more. That entry explained the steps to create a Web application, deployed on GlassFish V2, and contained a jMaki-wrapped Yahoo Data Table widget pulling data from JavaDB using the JPA.

The main reason for this is because jMaki data models have evolved since I wrote the original entry and are now formalized. Here is the delta from the previous entry to make it working:

  1. Use the following code in bullet 6 instead:

    <%@ page import="java.util.\*" %>
    <%@ page import="server.Company" %>
    <%@ page import="javax.persistence.\*" %>

    <%
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("jmaki-jpaPU");
      EntityManager em = emf.createEntityManager();

      List<Company> list = em.createQuery("select c from Company c").getResultList();

      out.println("{columns : [" +
        "{ label : 'Company Name', id : 'companyName'}," +
        "{ label :'Price', id : 'price'}," +
        "{ label :'Change', id : 'change'}," +
        "{ label :'% Change', id : 'pctChange'}," +
        "{ label :'Last Updated', id : 'lastUpdated'}" +
        "],");

      out.println("rows: [");
      for (int i=0; i<list.size(); i++) {
        Company c = list.get(i);
        out.print("{ companyName: '" + c.getCompanyname() + "'," +
          "price: '" + c.getPrice() + "'," +
          "change: '" + c.getChange() + "'," +
          "pctChange: '" + c.getPercentchange() + "'," +
          "lastUpdated: '" + c.getLastupdated() + "'}");
          if (i < list.size()-1)
            out.println(",");
          else
            out.println();
      }
      out.println("] }");
    %>
  2. The new generated code fragment in bullet 7.2 is now:

     <a:widget name="yahoo.dataTable"
      service="data.jsp" />

With jMaki 0.9.7.1, here is a snapshot of the updated web page:

And the updated NetBeans project is available here.

Technorati: jmaki glassfish jpa netbeans

Friday Jun 22, 2007

Dynamic Data in jMaki Widgets Using JPA

jMaki provides a rich set of data widgets that can be embedded in a web application. For most of the widgets to be useful, they need to be tied a database backend. For example consider a Table widget displaying data about your favorite stock tickers. This blog explains the steps to create such a Web application, deployed on GlassFish V2, that contains a jMaki-wrapped Yahoo Data Table widget pulling data from JavaDB.

If you are using a jMaki build higher than 0.96 (or dated after jul 30) then some steps in this entry need to be updated and described here. These steps are marked with "SEE THE UPDATED ENTRY".

  1. Create the Web application project
    1. In NetBeans IDE 5.5.1, create a new 'Web Application' project and name it as 'jmaki-jpa'.
    2. Choose GlassFish V2 as the Server as shown below:

    3. Add 'jMaki Ajax Framework' by clicking on 'Next' button while creating the project.
    4. Choose the 'Standard' layout as shown below:



      and click on 'Finish'.
  2. Configure the Database
    1. In NetBeans IDE, 'Runtime' tab, expand Databases, connect to the default database (with the URL 'jdbc:derby://localhost:1527/sample [app on APP]'). Specify the username 'app' and password 'app'.
    2. Right-click on the newly created connection and select 'Execute Command...' and enter the following query to create the table definition:

      create table COMPANY (id int,
                            companyName varchar(255),
                            price float,
                            change float,
                            percentChange float,
                            lastUpdated varchar(50),
                            PRIMARY KEY (id))
    3. Right-click on the database connection, select 'Refresh' to see the newly created table in the Tables tree. Select the 'COMPANY' table, right-click and select 'Execute Command...' and enter:

      insert into COMPANY values (1, 'A Co', 71.72, 0.02, 0.03, 'Jan 1, 2007, 10:00am' );
      insert into COMPANY values (2, 'B Inc', 29.01, 0.42, 1.47, 'Feb 1, 2007, 10:00am' );
      insert into COMPANY values (3, 'C Group Inc', 83.81, 0.28, 0.34, 'Mar 1, 2007, 10:00am' );
      insert into COMPANY values (4, 'D Company', 52.55, 0.01, 0.02, 'Apr 1, 2007, 10:00am' );

      Now our database structures are created and populated.
  3. Create the JPA (Java Persistence API) Entity class that maps to the database.
    1. In the projects window, select the project 'jmaki-jpa', right-click and select 'New' and choose 'Entity Classes From Database...'.
    2. Select 'jdbc/sample' as 'Data Source'.
    3. Select 'COMPANY' in 'Available Tables' and click on 'Add' and enter the values as shown below:



      and click on 'Next'.
    4. Specify the package name as 'server' as shown below:

    5. Click on 'Create Persistence Unit...' to create the persistence unit and enter the values as shown below:



      and click on 'Create'.

    and click on 'Finish'.

  4. Configure Persistence Unit
    1. In your project, expand 'Configuration Files' and open 'persistence.xml'.
    2. Click on 'Add Class' button and click on 'Cancel' button. For some reason the entity classes are not loaded during the first time.
    3. Click again on 'Add Class' button and choose 'server.Company' class and click 'OK'. This will ensure that the generated entity class is explicitly recognized by the EntityManagerFactory.
  5. In your project, right-click on 'Web Pages', select 'New' and then 'JSP...'. Give the name as 'data' as shown:



    and then click on 'Finish'.
  6. SEE THE UPDATED ENTRY - Replace the entire content of template 'data.jsp' with the following:

    <%@ page import="java.util.\*" %>
    <%@ page import="server.Company" %>
    <%@ page import="javax.persistence.\*" %>

    <%
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jmaki-jpaPU");
        EntityManager em = emf.createEntityManager();

        List<Company> list = em.createQuery("select c from Company c").getResultList();

        out.println("[");
        for (int i=0; i<list.size(); i++) {
            Company c = list.get(i);
            out.println("['" + c.getCompanyname() + "'," +
                c.getPrice() + "," + c.getChange() + "," +
                c.getPercentchange() + ",'" + c.getLastupdated() +
                "']");
            if (i < list.size()-1)
                out.println(",");
        }
        out.println("]");
    %>
  7. Add and Configure jMaki widget
    1. In the generated 'index.jsp', drag-and-drop a 'Yahoo Data Table' widget from the jMaki Palette in the 'Main Content Area'.
    2. SEE THE UPDATED ENTRY - Change the generated code fragment from:

      <a:widget name="yahoo.dataTable" args="{
          columns :[
              {title : 'Company', width : 200, locked:false},
              {title : 'Price', width : 75, renderer: 'usMoney'},
              {title : 'Change', width : 75, renderer: 'change'},
              {title : '% Change', width : 75, renderer: 'pctChange'},
              {title : 'Last Updated', width : 85, renderer: 'italic'}
          ]}"
          value="[
              ['A Co',71.72,0.02,0.03,'9/1 12:00am'],
              ['B Inc',29.01,0.42,1.47,'9/1 12:00am'],
              ['C Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
              ['D Company',52.55,0.01,0.02,'9/1 12:00am']
          ]" />


      to

      <a:widget name="yahoo.dataTable" args="{
          columns :[
              {title : 'Company', width : 200, locked:false},
              {title : 'Price', width : 75, renderer: 'usMoney'},
              {title : 'Change', width : 75, renderer: 'change'},
              {title : '% Change', width : 75, renderer: 'pctChange'},
              {title : 'Last Updated', width : 85, renderer: 'italic'}
          ]}"
          service="data.jsp" />

      The new text is highlighted in bold. The 'service' attribute tells jMaki runtime to pick up the data for DataTable widget from 'data.jsp' instead of the static data.
  8. That's it! Click on the Green button in NetBeans IDE to run the project or default keyboard shortcut (F6). And your browser shows the application deployed as:



    This jMaki-wrapped Yahoo Data Table widget is pulling data from JavaDB.

UPDATED: SEE THE UPDATED ENTRY - Based upon a user request, a NetBeans project for this sample can be opened via Java WebStart here. Alternatively, you can download the project and view at your own ease. Thanks to Geertjan for the tip!

Technorati: jmaki glassfish jpa netbeans

Tuesday Jun 19, 2007

Hello JPA World!

After much discussion, I was able to finally create a simple "Hello JPA World" example that uses Java Persistence API (JPA) to store and retrieve data from JavaDB from a Servlet deployed on GlassFish V2 b50 using NetBeans IDE 5.5.1. This blog describes the steps, in detail, on how to create this sample.

  1. In NetBeans IDE, create a new Web project and name it as "HelloJPA".
  2. Create an Entity class. Right-click the project, select 'New', 'Entity Class ...'. Specify the values as shown below:

    1. Create a new Persistence Unit by selecting 'Create Persistence Unit ...' and entering values as shown below:



      and click on 'Create'.

    and click on 'Finish'.

  3. Expand 'Configuration Files', open 'persistence.xml', click 'Add Class ...', select 'server.Company' and click on 'OK'. The 'persistence.xml' will look like:

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="1.0"
        xmlns="http://java.sun.com/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
        <persistence-unit name="HelloJPAPU" transaction-type="RESOURCE_LOCAL">
            <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
            <non-jta-data-source>jdbc/sample</non-jta-data-source>
            <class>server.Company</class>
            <properties>
                <property name="toplink.ddl-generation" value="drop-and-create-tables"/>
            </properties>
        </persistence-unit>
    </persistence>
  4. Add the following fields to the newly generated 'Company.java':

    private String companyName;
    private float price;
    private float change;
    private float percentChange;
    private String lastUpdated;
  5. Generate getters/setters for each field by selecting the newly added fields, right-click on the selected text, select 'Refactor', 'Encapsulate Fields ...' and choose the getter/setters for each field as shown below:

  6. Add a constructor to 'Company' class as follows:

    public Company(String companyName, float price, float change, float percentChange, String lastUpdated) {
        this.companyName = companyName;
        this.price = price;
        this.change = change;
        this.percentChange = percentChange;
        this.lastUpdated = lastUpdated;
    }
  7. Change the toString method in Company to:

    return "server.Company[id=" + id + ", lastUpdated=" + lastUpdated + "]";
  8. Add a new Servlet by right-click on the Project, select New, 'Servlet ...' as shown below:



    and click on 'Finish'.
  9. Update the generated Servlet template code such that it looks like:

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        beginHTML(out);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("HelloJPAPU");
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();
        out.println("<h1>Hello JPA World!</h1>");

        Company c = new Company("AAA Co", (float)10.0, (float)2.0, (float)10.0, new Date().toString());
        em.persist(c); // persisting to the source
        em.getTransaction().commit(); // now committed

        List list = em.createQuery(
            "select c from Company c where c.companyName = :companyName")
            .setParameter("companyName", c.getCompanyName()).getResultList();

        out.println("<b>Total Companies: " + list.size() + "</b><br>");
        for (int i=0; i<list.size(); i++) {
            out.println((Company) list.get(i) + "<br>");
        }

        endHTML(out);
    }

    void beginHTML(PrintWriter out) {
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello JPA World!</title>");
        out.println("</head>");
        out.println("<body>");
    }

    void endHTML(PrintWriter out) {
        out.println("</body>");
        out.println("</html>");
        out.close();
    }


    Fix the imports by using 'Alt+Shift+F' default keyboard shortcut.
  10. Right-click on the Project, select 'Properties', 'Run' Categories, change the Relative URL to '/Hello'.
  11. And that's it! Hit the Green button to run the project or the default keyboard shortcut of 'F6'. After re-loading the page twice, the following output will be seen in the browser window:


 

Technorati: jpa glassfish netbeans

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today