Ajax-based persistent object mapping

Map JavaScript objects to server data using Ajax with Persevere

The Persevere persistent object framework brings persistent object mapping to the browser JavaScript environment. Object persistence has seen great popularity in the Java™ programming and Ruby worlds, and the dynamic JavaScript language is naturally well suited to mapping objects to persisted data. Persevere automates mapping and communication in Asynchronous JavaScript + XML (Ajax)-based Web applications in addition to simplifying much of the development challenge by providing a manageable data model, transparent client-server Ajax interchanges, automatic state change storage, and implicit transaction management.

Kristopher William Zyp (kriszyp@xucia.com), Software Developer, Xucia

Kristopher ZypKris Zyp is a software developer, a consultant, and the founder of Xucia. His main focus is in the area of JavaScript persistence, co-routines, and other language features. He has developed Persevere (a JavaScript persistent object mapping framework), Strands (a JavaScript co-routine and threading support), Authenteo (an Ajax-based Web content management system), and the JSPON specification along with open source tools for accessing JSPON sources.



13 November 2007

Virtually all applications use some form of persistence; that is, they save information for future execution. Generally, the ability to persist information for later retrieval is a critical aspect of applications, and as Web applications increasingly integrate user interaction and contribution, persistence becomes more important. However, persistence often requires saving state information in a way that's conceptually different from how the data exists in the execution of the program.

Within the execution of a program, state information is typically stored in objects (at least, in object-oriented programs) but persisted either into databases or into text- or character-based formats. The transformation of state information back and forth between these two paradigms can often require significant development work and is highly susceptible to errors. Persistent object-mapping strategies can provide automation for state storage and retrieval by mapping objects to persistent data. Such mapping can also provide a simple mechanism for accessing persistent state and saving that state.

Persisting information
It's critical for a Web 2.0 application to have the ability to persist information for later retrieval. Learn about the Persevere persistent object framework and see how it automates mapping and communication in your Ajax-based Web applications.

Persistence mapping has enjoyed great popularity in other languages (for example, Active Record in Ruby on Rails and Hibernate in the Java language) because of the improved manageability of working with persistent objects. With Ajax-based applications, the process of saving information from data at the browser is even more complicated because it's necessary to serialize and send the data back to the server. The server must then retrieve the data and perform the storage process into its persistent stores.

Orthogonal persistence simplifies persistence even further by automating the entire storage process. Any changes to persistent mapped objects are automatically propagated to the persistent storage: No manual saving is necessary.

The tools

This article shows you how to use the Persevere JavaScript framework, the remote persistent object mapping framework for the JavaScript language with orthogonal persistence support. Persevere maps JavaScript objects to remotely persisted data stores by using JavaScript Object Notation (JSON) with standard Representational State Transfer (REST) HTTP methods following the JSON for persistence (JSPON) specification. You also learn how to use Persevere JavaScript client in conjunction with the Persevere server, which automates the process of exposing databases and other persistent stores as JSON REST services. By using Persevere, you can access and modify data on the server simply by accessing and modifying JavaScript objects in the browser. Persevere automates the necessary serialization, Ajax calls, and deserialization.

Accompanying this article is a demonstration of how to use persistent object mapping in JavaScript code to build a simple blog. To create your blog, you use the Persevere client framework in combination with the Persevere server. Using Persevere, you can access and manipulate persisted data using regular JavaScript objects and properties. Your persistent data source will be the default object database storage that the Persevere server provides. The Persevere server also provides the database mapping on the server to JSON, the format by which the Persevere client and server communicate. You do the majority of object access and modification through persistent object mapping by using simple JavaScript code and an API. (You can find links to all the tools in this article in the Resources section. You can download the blog.zip package from the Download section in this article.) Figure 1 illustrates this persistent Service-Oriented Architecture (SOA):

Figure 1. Persevere persistent JSON SOA
Persevere persistent JSON SOA

Note: Persevere implements the open API for persistence in JavaScript defined at persistentjavascript.org (see Resources for a link).

Getting started

To begin, download and set up Persevere (see Resources for links to the files). The Persevere server comes with the client, so you only need to download the Persevere server. You can download the Persevere server with Apache Tomcat ready to run or simply as a Java 2 Platform, Enterprise Edition (J2EE) Web application. If you download the Tomcat installation, go to the bin folder and run startup to start the server.

With a persisted object graph, you can access and modify objects and changes will be persisted. Persevere comes with a persistent object browser (the JSPON browser), which you can use for just that purpose. In addition, the Persevere server supports dynamic expandable object persistent storage, so you don't need to create any tables to generate your blog. To start the construction of your blog, open the browser (http://localhost:8080/browser.html, if you're using the default Tomcat setup), and then simply create a blog array object, as shown in Figure 2.(I like to make the blog object a property of the root object):

Figure 2. Creating the blog array object
Creating the blog array object

Accessing Persevere's persistence capabilities

Several alternative approaches for accessing the persistence capabilities of Persevere are available. Preprocessing is only necessary for supporting lazy loading of properties through standard JavaScript property syntax and for automatic saving of property changes. However, full persistence object mapping with explicit saving and lazy loading capabilities is still available without preprocessing by using the Persistent JavaScript API. Also, you can perform preprocessing on the server or during a build, which should be done for production applications for best performance. Alternately, Persevere can run natively with all features (without preprocessing) in JavaScript V1.7 (Firefox V2.0 and later), and you can use preprocessing for browsers that don't have JavaScript V1.7 support. Refer to the Persevere documentation (see Resources for a link) for more information about using these development methods and build processes.

The root object is a fixed object from which you can reach all the persisted object graphs of the dynamic object database. To do this:

  1. Open http://localhost:8080/browser.html?id=root in a browser. (Alternately, you can open http://localhost:8080/browser.html, click Load New Object, and then type root.)
  2. Click New Property to add a property to the root object. Use the field name blog, and then create an array object. This object simply represents the list of the blog posts.
  3. Click the blog object, and then click New Property; choose the property name name, and then type a name for your blog.
  4. Save your changes.

Persistence-aware JavaScript code

You use the Persevere framework to access the persistent objects. With the Persevere framework, you must preprocess the JavaScript code to add lazy loading and orthogonal persistence support to it. You can do this most easily by calling persevere.loadScript to load the JavaScript file. (See the sidebar Accessing Persevere's persistence capabilities for alternate methods.) With Persevere, you use a .pjs (rather than a .js) extension to indicate that the file is a JavaScript file that should have full persistence support. The loadScript() function then performs the necessary processing.

In this example, you simply use the default loadScript functionality, which performs the preprocessing on the client. To illustrate the capability of orthogonal persistence, you could write a function that would change the name of your blog, as shown in Listing 1:

Listing 1. Create the HTML code for a single blog post
function changeBlogName(blog) {
	blog.name = prompt("Enter a new name for our blog"); 
}

Create your blog

Next, create a simple HTML/JavaScript interface for generating a new post in your blog. First, you create a file called blog.html and put it in the root directory of your Web application (webapps/ROOT/ in the default Tomcat installation). Then, you create a file called blog.pjs with all the persistence-capable JavaScript code. Listing 2 shows the HTML code for your blog application:

Listing 2. blog.html
<html>
  <head>
    <script src="js/persevere.js"></script> <!--include the persistence framework -->
    <link rel="stylesheet" type="text/css" href="blog.css"/>
  </head>
  <body>
    <a href="#" onclick="renderBlog()">Home</a>
    <div id="blogDiv"></div> <!-- we render the blog in this div later -->
    <div id="newPost">
      <h3>Enter a new post</h3>
      <div> 
        Content:<textarea id="newPostContent">new post</textarea>
      </div> 
      <button onclick="addBlogPost()">Post</button>
    </div>
    <script>
      persevere.loadScript("blog.pjs");
    </script>
  </body>
</html>

Now, you can write a simple JavaScript function in blog.pjs to add a new post. Adding a new post creates a new JavaScript object that represents the new post. You then add that new post to the persisted blog array object. While most of the work is done with standard JavaScript code, you do need to get the root object to access the blog object. You do this by calling pjs.load("root"). The load() function is defined by the Persistent JavaScript API to load an object by ID; root is an alias ID for the root object of the object database. Listing 3 shows the function for adding a new blog post:

Listing 3. The addBlogPost() function
function addBlogPost() {
	var blog = pjs.load("root").blog;
	var newPost = {
		title : "new post",
		content : document.getElementById('newPostContent').value,
		created : new Date(),
		comments : [] // an empty array of comments
	};
	blog.push(newPost); // blog is an array, so we can add a new post to the top 
			// of the array using standard array functions
	newPost.title = prompt("What would you like to title the new post?",newPost.title);
}

When addBlogPost() is called, the new post is added to the blog; the Persevere framework automatically wraps the entire operation in a transaction and sends it to the server to be persisted. You don't need to call any save or persist methods when using orthogonal persistence; the property and array modifications are automatically saved.

Next, write a function to display the posts in the blog. To modularize your code, you must write two functions: one to render the entire blog and one to render just a single post. Listing 4 shows the JavaScript code for displaying these posts:

Listing 4. Create the HTML code for the blog
function renderBlog() {
  var blog = pjs.load("root").blog; // get the blog object, the list of the postings
  blogElement = document.getElementById("blogDiv");
  blogElement.innerHTML = ""; // clear it out
  for (var i =0; i < blog.length;i++) // iterate through blog posts, render each one
    renderPost(blog[i],blogElement.appendChild(document.createElement('div'))); 
}

You use a simple for loop to iterate through the posts in the blog. Persevere does lazy loading so that only the necessary data is downloaded. When an uninitialized property is accessed, Persevere automatically requests the correct data from the server and then resumes execution. Persevere uses continuations to suspend and resume execution so that asynchronous requests are made to retrieve data from the server (so the browser doesn't lock up because of synchronous requests).

Now, to render each post, type the code shown in Listing 5:

Listing 5. Create the HTML code for a single blog post
function renderPost(post,postDiv) {
  var html = "<h2>" + post.title + "</h2>" 
    + "<h4>" + post.created + "</h4><p>" + post.content + "</p>"; 
  postDiv.innerHTML = html;
}

For new posts to show up immediately, when the page loads, and when you call addBlogPost, add a call to renderBlog() at the end of the addBlogPost() function and at the end of blog.pjs. Also, you may want to use the unshift() array method instead of the push() method to add the post to the top of the list instead of the bottom.

Now use your new blog interface to add posts to the blog, and you have a functional blog. After adding a couple of posts, your blog should look something like Figure 3:

Figure 3. Your new blog
Your new blog

Go back to the persistent object browser to view the data that was saved. Figure 4 shows the browser following the posts:

Figure 4. Persisted data following the blog posts
Persisted data following the blog posts

Commenting on blog entries

You could add functionality for posting comments on blog posts. To do so, add an onclick event for clicking a blog post, which brings up a single blog posting with its comments. Then, add a comment form for the single post view. Next, add comments to the blog post in a manner similar to how you added a post to the blog object. You can get the code for adding the comments functionality from the Download section. You can also refer to the Persevere documentation for more information about when Persevere treats properties as transient (that is, not saved) or persistent.

By using the persistent object mapping capabilities of Persevere and the dynamic object creation capabilities of the Persevere server, you can easily create the persistence necessary for a blog. Notice all the steps you did not have to perform to create a blog: You didn't have to create database tables, write any Ajax calls, write any Structured Query Language (SQL) code, modify any configuration files, or create any controllers. You built this blog with simple, straightforward HTML and JavaScript code.

Security

The blog you've built doesn't have any security yet: It's completely open for anyone to post to. In most Web applications, you would want to restrict posting access and other forms of data modification. The logic for your blog is in a client-side JavaScript script, and security should never be solely enforced on the client.

The Persevere server has built-in, data-centric security capabilities that are enforced on the server. In most Web application development scenarios, convenience often leads developers to implement security in their application logic. However, this is a hazardous approach for larger Web applications because as the application logic grows, so does the attack vector. The more complicated the application logic, the more chances there are for attackers to find a hole. By using data-centric security, you separate security from the application logic, thereby making security more manageable.

When first installed, the Persevere server starts in unsecured mode. To enable security, open the persistent object browser (http://localhost:8080/browser.html) and click Sign in to create a new user. When the new user has been created, security is turned on, and the new user is designated as the super user. With security enabled, the default security level is for the persisted objects to be readable by the public and writable by the super user (the newly created user). Refer to the Persevere server documentation (see Resources) for more information about specifying access levels on different persisted objects.

Relational databases

Perhaps the most common form of persistent object mapping is object relational mapping. While you've seen persistent object mapping with client-created data structures in this article, the Persevere server supports traditional relational databases and exposing SQL database table data through JSON REST services for persistent object mapping with Persevere. To see an example of an SQL database table, open the persistent object browser and load the exampledatabasetable/ object. You can load this data from a JavaScript file with the load() function and the table will be accessible as a normal JavaScript array. You could easily modify data in the table by using the JavaScript code shown in Listing 6:

Listing 6. JavaScript interaction with a relational database
function changeNameForFirstRow() {
  pjs.load("exampledatabasetable/")[0].name="New name";
}

This statement loads the data from the table and changes the value of the name column in the first row to a new value. It is also possible to replace the object database storage used in the blog example with relational database storage. Because of the object abstraction, if the relational database storage is set up correctly, you could change the blog property to refer to the relational database storage, and the blog application would work exactly the same without changing a single line of code.

Additional Persevere capabilities

Persevere includes support for defining JSON Schemas. JSON Schemas define the acceptable types for the values of properties. For example, you could specify that the title property must be a string or that a comment item must have author, created, and content properties. This is analogous to Document Type Definitions (DTDs) in XML or classes in object-oriented languages. Structures can also define whether properties are persistent or transient (that is, not saved when changed). Refer to the Persevere documentation (see Resources), as it's important to understand the rules that Persevere uses when it persists a property.

Accessing cross-domain data with Persevere

Persevere supports building cross-domain object graphs that can span persisted object graphs from different servers. From the persistent object browser, create a new property, choose Existing by ID, and then type a URL for a JSON object.

Transactions

Persevere implicitly groups modifications into transactions. All modifications that take place within the handling of one event (such as modifications done in addBlogPost() because of the onclick event) are grouped into one transaction. Because all JavaScript processing within the browser happens within the context of an event, this behavior provides a convenient and generally logical approach to transaction handling.

However, it may be necessary at times to have further control of transactions. The Persistent JavaScript API defines two methods for controlling transactions: pjs.commit() and pjs.rollback(). The commit() method immediately attempts to commit the changes that have been made. The rollback() method will undo all the changes made since the start of the event or since the last commit. For example, you could add a confirmation at the end of the addBlog() function that would undo the posting, if cancelled, as shown in Listing 7:

Listing 7. Controlling transactions
function addBlogPost() {
  ...
  if (!confirm("Are you sure you want to create this post?"))
    pjs.rollback();
}

Alternate servers

The Persevere JavaScript client doesn't rely on the Persevere server. Because all communication with the server is based upon the standard HTTP methods and JSON format, it's not difficult to use Persevere to do persistent object mapping with PHP, Ruby, or other technologies. While this article does not cover HTTP and JSPON specifics, the server simply needs to deliver JSON and handle standard HTTP (PUT, POST, and DELETE) modification requests according to the JSPON specification.

Persisted functions and distributed computing

Persevere supports persisting functions or methods into dynamic persistent objects. With the blog application, you could have created all the functions you wrote as methods of the blog persistent object. Using this approach, the entire application logic could be stored in the same persistent storage as the data. Because the Persevere server uses an internal JavaScript environment (see Figure 1) to map persisted objects, persisted functions can be executed on the server just as they can on the client.

Because the Persevere server also implements Persistent JavaScript (see Resources), the functions can access the same persisted data in the same manner as on the client and, by using JSON-RPC, can even transparently call functions in the remote environment with normal JavaScript call syntax. This functionality can provide a more consistent environment for applications because a single storage medium is used for logic and data. It also provides a "live" real time for programming. Functions can be written, tested, and saved on-the-fly in the persistent object browser.

The Ajax resource center
Peruse the developerWorks Ajax resource center, where you'll find free tools, code, and information to get you started developing Ajax applications. The active moderated discussion forum might just have the answers to the questions you have right now.

Conclusion

By using orthogonal persistent object mapping, you can rapidly develop powerful Ajax applications by using simple, familiar JavaScript code. The complexity of writing Ajax requests, serialization, and database interaction can easily be handled by Persevere to provide object-oriented access to persisted data for rapid application development.


Download

DescriptionNameSize
Example blog application1wa-aj-objmap.zip2KB

Note

  1. This blog was written in 49 lines of JavaScript code.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


  • developerWorks Labs

    Experiment with new directions in software development.

  • developerWorks newsletters

    Read and subscribe for the best and latest technical info to help you deal with your development challenges.

  • JazzHub

    Software development in the cloud. Register today and get free private projects through 2014.

  • IBM evaluation software

    Evaluate IBM software and solutions, and transform challenges into opportunities.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=269070
ArticleTitle=Ajax-based persistent object mapping
publish-date=11132007