Building An Ajax-Enabled Web Application Using Phobos and jMaki

by Roberto Chinnici

Phobos is a lightweight, scripting-friendly, web application environment that runs on the Java platform. Using Phobos, you can take advantage of the benefits offered by scripting languages and leverage the power of the Java platform. Being scripting-friendly, Phobos provides a programming environment that fosters rapid application development. The primary language supported by Phobos is JavaScript, which Phobos supports using the Mozilla Rhino scripting engine.

Scripting languages can be powerful and convenient. They also enable an interactive form of development in which you can progressively refine the behavior of an application until it is ready for deployment and use. This is especially important in developing Ajax-enabled web applications. These applications typically require a lot of interactive in-browser testing and refinement. In addition to enabling an interactive mode of development, Phobos allows scripting developers to call into any Java code at any time. As a scripting developer, you can reuse APIs, libraries, and frameworks that run on the Java platform. Using Phobos offers a "best of both worlds" approach, in which you can write "soft" -- often changing code -- in a scripting language, and write "hard" -- rarely modified code -- in a compiled language such as the Java programming language.

In this tip, you'll use Phobos and jMaki to create a simple Ajax-enabled web application. The jMaki framework is a lightweight framework for creating Web 2.0 applications using standards-based technologies such as CSS, HTML, and JavaScript. For an introduction to jMaki, see the January 27, 2007 Tech Tip Introduction to jMaki.

You will build the application using the NetBeans IDE and deploy the application to the GlassFish application server. The tip assumes that you have NetBeans IDE 6.1 and the GlassFish v2ur2 application server installed. You can install them together by installing the Web & Java EE version of NetBeans IDE 6.1.

A package that contains the code for the sample application accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package).

The Phobos Framework

The key to understanding how a Phobos application works is to become familiar with the underlying framework.

In Phobos, whenever a request comes in to the servlet container, the container invokes the Phobos servlet. The Phobos servlet then delegates the handling of the request to the Phobos framework, which is written in JavaScript. The framework attempts to match the request URI with a certain number of predefined patterns. When a match is found, the framework invokes the script or controller associated with that pattern.

In Phobos there are many ways to handle a HTTP request. The simplest way is by writing a plain script which has access to two predefined variables called request and response. These variables hold a reference to the HttpServletRequest and HttpServletResponse that the servlet container creates. The user-written script can then perform the appropriate request-handling logic and produce a response.

Although scripts are in principle sufficient to cover every task, it is better to follow a more structured approach based on the MVC (model-view-controller) pattern. In Phobos, all three components are written in JavaScript. The controller is a JavaScript class hosted inside a script whose name must match that of the controller. The view is an Embedded JavaScript (EJS) file, that is, a file that contains HTML with embedded JavaScript statements and expressions. The definition of the model is left to the application. Typically, the model consists of a mix of JavaScript and Java objects.

To learn more about the Phobos framework, see An Overview of Phobos.

The Application

This tip uses Phobos to reimplement the PHP application that Ludovic Champenois presented in the June 16, 2008 Tech Tip Using jMaki with PHP on OpenSolaris. See "The Sample Application" section of that tip for a description of the application. Figure 1 shows a web page displayed by that application.

A Web Page from the PHP Application

Figure 1. A Web Page from the PHP Application

Notice that the PHP application uses MySQL as the database. However, the Phobos version of the application uses JavaDB, rather than MySQL. You'll find the Java DB database bundled with GlassFish v2ur2.

Install the Phobos Plugins in NetBeans IDE 6.1

Before building the application, you need to add support for Phobos to the NetBeans 6.1 IDE. This support is provided by Phobos-related plugins which include functionality such as a new Phobos project type and a syntax-aware editor for EJS pages

To install the Phobos plugins:

  1. Select Plugins in the Tools menu of the IDE. This opens the Plugins window.
  2. Click the Available Plugins tab if it's not already selected.
  3. Check the checkboxes for the EJS and NetBeans 6 Phobos support plugins in the Phobos category as shown in Figure 2.

    Selecting the Phobos Plugins

    Figure 2. Selecting the Phobos Plugins

  4. Click the Install button. If it is not already installed, NetBeans will also install the jMaki Ajax Support plugin.

Configure the Java DB Database

Next, let's create a new database for the application.

  1. Start Java DB as follows:
    • Click the Services tab in the NetBeans IDE.
    • Expand the databases node. You should see Java DB in the list of databases.
    • Right-mouse click on Java DB and select Start Server.
  2. Create a Java DB database as follows:
    • Right-mouse click on Java DB and select Create Database. This will open a Create Java DB Database window.
    • Enter the database name, data. This is the database name preconfigured in Phobos. (See the section Connecting to the Database for more information about database configuration parameters that are predefined in Phobos.) Also, enter a user name, APP, and a password, APP. (These parameters, as well as the type of database to access, are fully configurable and customizable. You can specify different parameters or a different database type when you develop the application or later when the application is deployed.)
    • Click the O.K. button.
  3. Expand the Drivers node. You should see a driver for jdbc:derby://localhost:1527/data as shown in Figure 3. This is the driver for the data database.

    Driver list

    Figure 3. Driver List

Create a Project for the Web Application

Now that you have added the Phobos plugins to the NetBeans IDE and created and configured a Java DB database, it's time to create the application. Start by creating a new project in the NetBeans IDE, as follows:

  1. Select New Project in the File menu of the IDE. This opens the New Project window.
  2. Select Web for the category and Web Application for the project in the New Project window and click the Next button. This opens the Name and Location panel.
  3. Enter PhobosSample in the name field and select an appropriate location for the project. Then click the Next button. This opens the Server and Settings panel.
  4. Ensure that GlassFish V2 is selected as the server and Java EE 5 as the Java EE version. Then click the Next button. This opens the Frameworks panel.
  5. Select jMaki Ajax Framework and Phobos Runtime as a Web Application Extension in the Frameworks field. When you select the jMaki Ajax framework, the panel displays a CSS Layout section. Choose No CSS Style in the CSS Layout section, as shown in Figure 4.

    Selecting the Phobos and jMaki Frameworks

    Figure 4. Selecting the Phobos and jMaki Frameworks

  6. Click the Finish button to create the project.
  7. Expand the new PhobosSample project in the Projects tab. Figure 5 shows that in addition to familiar elements that comprise a web application project, such as web pages and configuration files, the PhobosSample project includes Phobos-specific elements that reside in the Phobos Application folder.

    PhobosSample Project

    Figure 5. PhobosSample Project

Customize the Scripts for the Application

A Phobos application contains a set of scripts that are executed in response to web requests. Additionally, it contains EJS pages that are used as views. These pages contain code snippets written in JavaScript that are executed when the page is rendered. This allows dynamic content to be generated.

The application you created contains a predefined model/view pair. If you run the application as it currently exists, you will see the web page displayed in Figure 6. You can see this default page by right-clicking PhobosSample in the Projects tab and selecting Run.

Default Page Displayed by a Phobos Application

Figure 6. Default Page Displayed by a Phobos Application

You need to customize the model and view to produce the results you want for the application. That requires you to update a number of pregenerated scripts. In addition, you need to create some other scripts to do things such as connect to the database. The remainder of the tip focuses on these scripting actions.

Connect to the Database

For the application to work, it needs to connect to the database. In Phobos, you implement the connection using a startup script. This is a special script that the Phobos runtime executes when the web application is initialized. The script initializes the database layer, connects to the database, and checks that the table required by the application exists. If the table does not exist, the script creates it.

Note that these actions were performed by the setup.php page in the PHP version of the application. In the Phobos version of the application, there is no need to manually point the browser to a setup page because the application will do that automatically at startup.

To create the startup script:

  1. Right-mouse click on the module package under Phobos Application and select New then Other. This open a New File window.
  2. Select Scripting as the category and Phobos Script as the file type, then click the Next button. This opens a Name and Location panel.
  3. Enter application as the script file name. Ensure that the full name of the resource in the Created File field is
    web/WEB-INF/application/module/application.js.
  4. Click the Finish button to create the script file. You will see the new file, application.js, added under the module package.
  5. Open the application.js file.Replace the contents of the application.js file with the following:
       library.common.define(module, "application", function() {
    
           this.onStartup = function() {
    
               application.options.database.preferred = "client";
               library.db.initialize(application.datasource.client);
    
               var sql = new library.db.SqlHelper();
               library.db.using(sql, function() {
                   try {
                       var result = sql.query({query: "SELECT COUNT(\*) FROM products"});
                   }
                   catch (e) {
                       // must create the table
                       sql.execute("CREATE TABLE products (" +
                                      "id int NOT NULL GENERATED ALWAYS AS IDENTITY PRIMARY KEY," +
                                      "name varchar(15) NOT NULL," +
                                      "category varchar(20) NOT NULL," +
                                      "price varchar(20) NOT NULL)");
    
                   }
               });
           }
       });
    
     

The library.common.define function call that wraps the code below it is the Phobos way of creating a package. You can think of that line as being equivalent to the statement package module.application; in the Java programming language. The JavaScript language does not have a native package construct. Because of that , Phobos (and any other JavaScript library) had to define its own construct.

The body of the package defines and exports the onStartup function. That function is invoked by the Phobos runtime when the application starts up. The onStartup function selects the client database as the preferred database and initializes it. Its configuration parameters are predefined in Phobos as follows:

   application.datasource.client = {
       dataSourceClassName: "org.apache.derby.jdbc.ClientConnectionPoolDataSource",
       username: "APP",
       password: "APP",
       properties: {
             serverName: "localhost",
             portNumber: "1527",
             connectionAttributes: ";create=true",
             databaseName: "data"
       }
   }
 

As you can see, the databaseName is data, which matches the name of the database you created earlier in the NetBeans IDE. If you want to use a different database, you need to copy the code shown above, paste it into the application module, and edit it to suit your needs.

The rest of the code in the application.js script tries to connect to the database and execute a SQL query. If it fails, it executes an SQL CREATE statement to create the table that holds the product information. This code uses the Phobos db library (library.db), which wraps JDBC inside a more JavaScript-friendly layer. You can get more details about this library in the Phobos Library Documentation.

Run the application again to ensure that the added code is correct and to test that the application can connect to the database. If everything is correct, you'll see the same page as before -- the contents of the page won't change until you modify the main controller for the application. In case of an error, you should see a Phobos error page with a complete stack trace.

Change the Main Page

If the application can connect to the database, it's time to start implementing the rest of the code, starting with the main page. This page is the equivalent of the index.php page in the PHP version of the application. However, there are some notable differences. Unlike PHP, Phobos uses a full MVC framework, so the application logic is split between view and controller.

Let's update the view first -- it's the main.ejs file in the view package under Phobos Application in the project. The contents of the view are similar to those of the index.php page in the PHP version. The main difference is that instead of using PHP snippets inside the <?php ?> sections, the view uses JavaScript snippets enclosed within <% %>.

Replace the code in the main.ejs file with the following:

   <html>
      <head>
         <title>Phobos Sample Application</title>
         <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
         <link rel="stylesheet" href="style.css">
      </head>
      <body>
      <br>Using the following form, you can add products that will be inserted in
      a JavaDB database, and displayed using the jMaki Ajax framework in the table below.<br>
      <br><br>
      <form action="#" >
      Product Name : <input id="name" name="name" type="text"><br>
      Product Category :<input id="category" name="category" type="text"><br>
      Product Price : <input id="price" name="price" type="text"><br>
      </form>
      <%
          library.jmaki.insert({
               component: "yahoo.button",
               value: { label : 'add a product' }
          });
      %>
      <br>
      <b><center>List of Products:</center></b><br><br>
      <%
          library.jmaki.insert({
              component: "yahoo.dataTable",
              args: {columns : [
                       { 'label' : 'name', 'id' : 'name'},
                       { 'label':'category', 'id' : 'category'},
                       { 'label': 'price ', 'id' : 'price'}
              ]},
              service: library.httpserver.makeUrl("/main/listProducts")
          });
      %>
      </body>
   </html>
 

As you paste the contents into the file, the NetBeans IDE highlights the JavaScript sections (by default, it uses a light-green color) to point out that the sections render dynamic content. This differentiates these sections from the rest of the page, which is rendered as entered.

Notice the library.jmaki.insert function in the main.ejs file. This function is used in Phobos to insert jMaki widgets. If you compare the function to the addWidget function in the index.php file of the PHP version of the application, you'll see that the arguments to both are similar. They also resemble the arguments that the jMaki JSP tag library expects. This makes the transition from PHP to Phobos straightforward.

One important difference between the way the Phobos version and the PHP version use jMaki widgets is that in Phobos the values and arguments (args) passed to the widget are real JavaScript objects, not strings. This highlights one of the most interesting features in Phobos. The client (that is, the scripts running inside the browser) and the server share a common language, JavaScript. This makes it easier to share data between the two tiers and eliminates a lot of translation work. In particular, JavaScript Object Notation (JSON) becomes a native format supported by both tiers, and so, is a particularly appealing data format.

Another thing to point out in the EJS page is the call to the library function, library.httpserver.makeUrl("/main/listProducts"). Phobos views are stored in a separate directory tree from static content, so that referencing other resources or even controllers from a view is error-prone. The library.httpserver.makeUrl function helps here because it allows you to specify an absolute URL as an argument. The URL is then replaced with the correct URL at runtime. In the example, the URL points to the main controller and triggers invocation of the listProducts method when it is accessed.

Add jMaki Glue

Because the application uses jMaki, you need to create a jMaki Glue file, glue.js, that "glues" the widgets on the page together using a publish-subscribe event model. Replace the contents of the glue file, resources/glue.js, which you can find under Web Pages in the project, with the following:

   // uncomment to turn on the logger
   jmaki.debug = false;
   // uncomment to show publish/subscribe messages
   jmaki.debugGlue = false;

   // map topic for the add Product button
   jmaki.subscribe("/yahoo/button/onClick", function(args) {
      // get the values of the 3 fields in the form:
      var name= document.getElementById("name").value;
      var category= document.getElementById("category").value;
      var price= document.getElementById("price").value;
      //do an ajax request to the add server side logic, with the correct params:
      jmaki.doAjax({
        url : "addProduct",
        method : "POST",
        content : {
          name : name,
          category : category,
          price : price
        },
        callback: function(req) {
        //in this call back, we just add a new row to the local jMaki table
        if (jmaki.trim(req.responseText) == "OK")
          jmaki.publish ('/yahoo/dataTable/addRow', {
                         value: {
                          name : name,
                          category : category,
                          price : price
                         }
          });
        else
         alert ("error adding a row: "+req.responseText);
       }
     });
   });
 

Update the Controller

The modified view relies on some additional behavior from the controller, the listProducts action, which you need to implement. However, first, let's rewrite the generated controller code to use more stylish, easier to read JavaScript. Replace the code in the main.js file in the controller package below Phobos Application with the following:

   library.common.define(controller, "main", function() {

        function Main() {
        }

        Main.prototype.show = function() {
            model = {};

            library.view.render("main.ejs");
        }

        // export the controller class
        this.Main = Main;
   });
 

Notice the similarity between this code and the code in the application.js file defined earlier. A controller is itself a module, but defined under the controller node in the project. A controller module defines a controller class, represented in JavaScript by a constructor function -- in this case Main. In addition, a controller class has one or more action methods, which in JavaScript are usually defined using the constructor prototype. The entire code is idiomatic, so it may appear strange at first.

You modified some code, so let's test it by running the application again. When you run the application, you should see the page displayed in Figure 7. Although part of the page renders properly, it also displays some error messages.

The Application Page Displaying Errors

Figure 7. The Application Page Displaying Errors

The reason for these errors is that the application is trying to insert some jMaki widgets into the page, but the resources for those widgets are not in the application. The easiest way to fix the problem is to do the following:

  1. Open the main.ejs view.
  2. Open the jMaki palette in the NetBeans IDE.
  3. Expand the jMaki Yahoo item in the jMaki palette.
  4. Drag and drop the jMaki Yahoo Button and Data Table widgets onto the page.

Figure 8 shows the part of the code generated by the drag and drop.

Code Generated by Dragging and Dropping jMaki Yahoo Widgets

Figure 8. Code Generated by Dragging and Dropping jMaki Yahoo Widgets

The jMaki palette automatically inserts some JavaScript snippets that call the library.jmaki.insert function. You can safely delete these snippets because the page already contains that code. A side benefit of dragging and dropping the widgets is that the necessary resources are added to the project. You can find the added resources under the Web Pages/resources/yahoo folder in the project.

Run the application again. As Figure 9 shows, the resource-related error messages have disappeared and the button near the top of the page is rendered correctly. However, at the bottom of the page, where the table should be, there is still an error message.

The Application Page Displaying a Failed to Load Data Error

Figure 9. The Application Page Displaying a Failed to Load Data Error

The error message is an indicator that the listProducts method in the controller has not yet been defined. Define it now by adding the following code inside the main.js script:

   Main.prototype.listProducts = function() {
       library.httpserver.sendJSON({rows: module.persistence.fetchProducts()});
   }
 

Add a Persistence Layer

In keeping up with the incremental nature of development in Phobos, let's now implement a persistence layer. This allows you to test the listProducts functionality in the browser. To do this, create a persistence.js module. You can follow the same process you did to create the application.js module, that is, the startup script.

After you create the persistence.js module, replace its contents with the following code:

   library.common.define(module, "persistence", function() {

       this.fetchProducts = function() {
               var sql = new library.db.SqlHelper();
               var products = [];
               library.db.using(sql, function() {
               products = sql.select({table: "products",
                                      columnMapping: columnMapping});
               });
               return products;
        };

        this.addProduct = function(product) {
            var sql = new library.db.SqlHelper();
            library.db.using(sql, function() {
                sql.insert({table: "products",
                            values: product,
                            propertyMapping: propertyMapping,
                            prepare: true});
            });
        };

        var columnMapping = {
          ID: "id",
          NAME: "name",
          CATEGORY: "category",
          PRICE: "price"
        };

        var propertyMapping = {
          id: "ID",
          name: "NAME",
          category: "CATEGORY",
          price: "PRICE"
        };
   });
 

As is the case in the startup script, the persistence module uses the Phobos db library to avoid having to directly interact with JDBC. The fetchProducts and addProducts functions are exported from the module by adding them to this object, which in this context is the module itself. The code to add a new product is simple. The code does not validate the input data. Neither does it handle exceptions, which are simply passed back to the caller. A production application would include many more checks to make sure no illegal or malicious data is sent to the database.

The two mapping objects at the bottom deserve an explanation. Because Java DB converts all column names to uppercase, if you retrieve data from the database you would find properties with the names NAME, CATEGORY, and PRICE. But if you examine the main.ejs page, you'll notice that it expects properties called name, category and price, respectively. It is easier to do the conversion in the database layer by specifying two mappings of column names to property names and back. This approach is also useful when you want to map names written in camel case that are commonly used in JavaScript, for example, streetAddress, to underscore-rich names that database professionals typically use, such as STREET_ADDRESS.

Now that the persistence layer is completed, you can test the addProducts controller action by pointing the browser to the URL http://localhost:8080/PhobosSample/main/listProducts. You should see the following text in the browser:

   {rows:[]}.

This is an expected result because the database table is currently empty.

Add the Products

When a user of the application clicks on the add a Product button to add a product, the application sends an Ajax POST request to the addProduct method of the main controller. It's now time to implement this method by adding the following code to the main.js controller script:

   Main.prototype.addProduct = function() {
        var params = library.httpserver.parseRequestParameters();
        if (params.name) {
            module.persistence.addProduct({name: params.name, category: params.category, price: params.price});
            library.httpserver.sendOk();
        }
        else {
            library.httpserver.sendNotFound();
        }
   }
 

This function uses the parseRequestParameters library function to parse the body of the POST request into a JavaScript object. The function then extracts the data from that object and passes it to the database layer. Here, because all the property names match, the function could have been designed to pass the params object directly. However, in general, it is safer to insulate the web-facing layer from the database layer. This avoids exposing the database to attacks by malicious clients.

Test the Application

The application is complete and ready for testing. Run it and try adding a few products. You should see the updates immediately reflected in the UI as shown in Figure 10.

Adding Products to the Database

Figure 10. Adding Products to the Database

You can also directly point the browser to the listProducts action at http://localhost:8080/PhobosSample/main/listProducts. This will display the data in JSON format:

   {"rows":[{"id":"1","category":"car","price":"15000","name":"fiat"},{"id":"2","category":"car","price":"18000","name":"subaru"}]}

 

Summary

This tip showed you how to build an Ajax-enabled application using Phobos and jMaki with the help of the NetBeans IDE.

For persistence, the application used a simple JDBC wrapper library provided with Phobos. If the data model were more complex, the application could have used a full-featured Java persistence library such as the Java Persistence API (JPA) or Hibernate. Because of the excellent interoperability between JavaScript and the Java programming language, developers can use the language most appropriate for each task without compromising productivity.

Further Reading

About the Author

Roberto Chinnici is a senior staff engineer at Sun Microsystems. He currently serves as the specification lead for the Java Platform, Enterprise Edition (Java EE) 6. Roberto is a long-standing advocate of scripting and dynamic languages on any platform and the creator of Phobos.

Comments:

excellent idea

Posted by imad on September 10, 2008 at 03:36 PM PDT #

This is very interesting for me and to start use Phobos I need to know a little bit more. Here is my questions:

if there are some performance issues comparing with typical web java oriented application?

My application using JPA and is it possible instead of java entity classes to use pure javascript entity classes?

To understand better the last question let put it another way:
Is it possible for simple CRUD oriented application to create and modified persistence entity classes on the fly in real-time (interactive mode) without even redeployment of the application?

Thank you very much for your excellent article.

Posted by Vladimir on September 23, 2008 at 02:16 PM PDT #

This is a good articel,
would U like To give me a book for study me at Home.(offline.), about this.. :)

Thank's

Posted by Adi Saputra on November 23, 2008 at 08:12 PM PST #

Post a Comment:
Comments are closed for this entry.
About

edort

Search

Archives
« April 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
   
       
Today