Persevere is an open source persistent object mapping framework for JavaScript in the browser. Persevere allows programmers to access, traverse, and manipulate persisted objects graphs easily with standard JavaScript syntax and intuitive Persistent JavaScript (PJS) API. Persevere implements the PJS API and maps JavaScript objects to persistent storage objects remotely through JSON based RESTful web services following the JSPON (JavaScript Persistent Object Notation) data interchange specification. Persevere accesses persisted object graphs provided through JSPON data sources which can represent various underlying sources such as database tables, XML files, web services, and object repositories, and such persisted graphs can even span domains. Persevere allows application code to be persisted and exist within these object graphs, to facilitate improved object oriented design and organization. Persevere can provide orthogonal persistence and lazy loading support with standard JavaScript property access and modification through JavaScript pre-processing, or it can provide these capabilities natively with JavaScript 1.7. Persevere is built on top of Strands and therefore supports coroutines to provide continuations and threading. With Persevere, you can use access objects that are mapped to persistent stores on a server with standard JavaScript. In this example we can see access and manipulation of an object:
var purchaseOrder = pjs.load(poId); // load an object by id var customerName = purchaseOrder.customer.name; // retrieve the customerís name purchaseOrder.quantity = 5; // change the quantity
In this example, Persevere loads the object from the server, and retrieves the customer name. If the customer object and the name string have not been loaded from the server, then it can automatically retrieve this information from the server using JSPON. Persevere handles all the Ajax interaction with the server transparently so that you donít need to worry about the remote requests. In the last line in the example, the persistent field for quantity is changed. Persevere can automatically send the data change back to the server as well for the appropriate field to be updated. In this example, the data could be representing database tables, such as a purchase order table that is related to a customer table. You can visit the Persistent JavaScript site for the full PJS API documentation. Additional Persevere functions are documented here.
Persevere supports three different coding methods. For the first two methods, files with persistence capabilities should be loaded by calling persevere.loadScript(scriptName). The loadScript will use the appropriate loading and processing mechanism. Here are the methods:
function getCustomerName(purchaseOrder) { var customerName = purchaseOrder.customer.name; // if customer or name is not loaded yet, it will be loaded automatically return customerName; }example-pjs.html has examples of using Persevere in this mode.
var getCustomerName = strand(function (purchaseOrder) { var customerName = yield purchaseOrder.customer.name; // if the name is not loaded yet, it will be loaded automatically returns(customerName); });The yield operator only needs to be used when accessing a property that may need to be lazy loaded requiring suspension, or when calling another function that might require suspension. JavaScript 1.7 files should have .js17 suffix. example-js17.html has examples of using Persevere in this mode. This is a good choice to combine the power of inline lazy loading, and still control of what is delivered to the browser (in Firefox) and allows flexibility and power of controlling which methods can suspend execution and when to save properties. With this level of control, higher performance can be acheived than the first approach.
persevere.precompiled=trueThere are two ways to compile .pjs files before they are sent to the browser:
To use the PJSCompiler in a build. Use the following syntax from the build directory: >java -classpath lib/js.jar;lib/persevere.jar com.xucia.persevere.PersevereCompiler js/myfile.pjs Use the provided PJSCompiler Java servlet. If you are using a J2EE application server, this is easiest approach. Add the persevere.jar and persevere-compiler.js to your classpath and add the following entry to your web.xml for your web application:
With these settings .pjs and .js17 will automatically be compiled for you when they are requested from the serverPersevereCompiler com.xucia.persevere.servlet.PersevereCompilerServlet PersevereCompiler *.js
Uncompiled code can not utilize transparent lazy loading and automatic object modification detection, nor coroutine capabilities. However, uncompiled code can utilize the PJS API in Persevere, and access all the persistent object mapping capabilities of Persevere. By default, Persevere uses asynchronous requests for certain methods and without pre-processing you must provide a callback handler if you want to resume execution after the completion of the method. To use asynchronous pjs library functions from uncompiled code looks like this:
pjs.load(poId,function(purchaseOrder) { // load purchase order pjs.get(purchaseOrder,"customer",function(customer) { // if the customer property is lazy loading, we must use the pjs.get method var customerName = customer.name; // assuming that name property is not lazy loaded purchaseOrder.quantity=5; pjs.save(purchaseOrder); }); });
The simplest way to use the PJS library from outside compiled code is by using synchronous mode. In synchronous mode, all requests to the server are done with synchronous calls and therefore there is no need for the functions to suspend and resume using continuations. However, in synchronous mode all calls to server cause the browser to lock up until the call is completed. This is generally a bad experience for users and therefore this approach is generally not recommended. To set synchronous mode:
pjs.synchronousMode = true;In synchronous mode you can utilize the pjs library functions as you would normally. For example to load a purchase order and get the customers name and set the quantity like in the earlier example we could write:
var purchaseOrder = pjs.load(poId); var customerName = pjs.get(purchaseOrder,"customer").name; ...Note, you should not mix synchronous and asynchronous mode. You must stay in one mode and not switch back and forth.
Most of the core functionality of Persevere is defined in the Persistent JavaScript API. The API is copied here for your reference:
Persistent JavaScript also defines an API for accessing and manipulating persistent objects. The API is contained in the pjs namespace.
Returns an object with the given id. All persisted objects should have an id, and this functions provides the ability to retrieve an object by its id. callback is an optional parameter if an asynchronous non-blocking operation is used to load the id. If available, callback will be called when the load is finished, and will be called with a single parameter, the value loaded.
This will set a persistent property of the given target object.
This will retrieve a property from the given target object. This should act essentially the same as target[key]. callback is an optional parameter if an asynchronous non-blocking operation is used to get the property (lazy loading). If available, callback will be called when the get is finished, and will be called with a single parameter, the value retrieved.
Commits all the changes that have been made in the current transaction. callback is an optional parameter if an asynchronous non-blocking operation is used to commit. If available, callback will be called when the commit is finished, and will be called with a single parameter, a boolean indicating the success of the operation.
Rolls back all the changes that have been made in the current transaction.
Prepares an object to be modified, this is not necessary if you using orthogonal persistence.
Saves the changes made to a persistent object in the current transaction.
Returns the access level of the current user on the given object. The access level should be an integer denoted a permission according to this table:
* 0 - none - can not access any of the fields on this object
* 1 - browse - can only read from a limited set of fields (usually name and basis)
* 2 - read - can read all the information from this object, but can not make any modifications to this object
* 3 - append - for list objects, entries can be appended, but no property modifications can be made
* 5 - write - any modifications can be made to this object
* Note that Persistent JavaScript is garbage collection based so there is no concept of deleting an object, only properties. Objects are deleted when all references to them are removed.
Returns the object id for the given object.
Returns true if the object has been persisted or is in the act of being persisted.
This function is called whenever the new operator is applied to an object (not a function). This returns a new object with a basis of the provided basis parameter.
Persevere also adds persistence capabilities to the core JavaScript array methods. Because Persevere supports lazy loading, Persevere adds an additional optional callback parameter to all the array methods to support situations where lazy loading is needed. For example if an array object had items that had not been initialized, one could call join this way:
myArray.join(',',function(returnValue){ alert('Here is the joined array: ' + returnValue); }In addition, provides several static methods on the Array constructor object. In particular, it is recommended to use the Array.forEach or Array.some for iterating through arrays. The Array.forEach automatically handles lazy loaded arrays properly, so it can easily be used in any programming mode (pre-processed or not) to iterate through an array. The Array.prototype.forEach offers the same functionality, and can be used in Firefox, but it is not available in IE. It also recommended that use Array.remove to remove elements from arrays that represent sets, like query results and database table data.
This function can be used to iterate through an array. For example:
Array.some(myArray,function(value,i) { ... // processing for each item return i >= 50; // when it is gets to 50 stop iterating },done); // done will be called when it is finished iterating
Removes the given value from the array (the first occurence if it exists multiple times)
Persevere is built on Strands, and therefore includes the full Strands API for coroutine and threading support. However these additional functions are also available:
This function should be used to load scripts for Persevere. If a .pjs file (JavaScript with persistence support) or .js17 file is loaded than client side compilation is performed if necessary (if it was compiled on the server, or and the browser supports doesn't support JavaScript 1.7).
persevere.compilerURL should be set to the correct URL for the compiler.js file. This defaults to "js/compiler.js" which should be correct for files that are in the root of the installation directory.
persevere.precompiled should be set to true if the compilation is done the server. persevere.setAutoSave(autoSave) This sets whether or not Persevere should automatically save changes made to persisted objects, without needing to call save (orthogonal persistence). This is on by default with Persistent JavaScript files (.pjs) and off by default with others (.js17 and .js files). This option is not supported in pre JavaScript 1.7 environments without pre-processing. If setAutoSave is called, it should be called after loading persevere.js and prior to loading any persistent objects or loading any other pre-processed scripts (.js17 or .pjs files):
If this is not enabled, one must call pjs.save(object) to save changes made to a persistent object. If this set, all scripts should be loaded with loadScript to ensure that any necessary compilation takes place. If setAutoSave is set to true and scripts are loaded without loadScript, they will behave differently in Firefox and IE. Firefox supports auto save natively without compilation, and IE does not, so property changes could be persisted in Firefox and not persisted in IE. If setAutoSave is set to false, scripts do not necessarily need to be loaded with loadScript for Firefox and IE to behave the same. persevere.request(url,params,operation) This makes an Ajax call to the server for the given url, with the given parameters. The parameters can be any objects, JSPON referencing and serialization is used to transfer the values. The operation parameter defines the status of what is happening for the UI. persevere.stringify(value) This method converts a value to a JSPON string representation. pjs.rpc(thisObj, params, methodName [, callback]):Object This will fire remote RPC call to the server.
Persevere supports mixing persistent and transient properties on objects. However, it is important to understand which properties are treated as persistent. Persistent and transient properties can be explicitly in the object structure. By creating an object structure in the JSPON browser, you can define for each property if it is persistent by setting the persistent property in the corresponding structure property. You can see Persistent JavaScript specification for more information. If the property is not explicity defined in the structure, properties will be treated as persistent if they were loaded from persistent information on the server. If any changes are made to these properties while in auto save mode, the changes will be automatically be persisted. Any new properties that are added will be treated as transient properties (no changes will be saved, even if in auto save), until pjs.save is called for the object. If pjs.save is called on an object, all properties, new and old, that are not explicitly defined as transient in the structure will be persisted.
Persevere comes with the JSPON persistent object browser, which is a free, open-source tool for browsing and manipulated JSPON persisted object graphs/data sources. The JSPON browser can be found at www.jspon.org/browser.html or downloaded at www.jspon.org/files/browser.zip . JSPON is a JSON extension for robust access and manipulation of persisted objects that includes definitions for how to support object and array identification, referencing, lazy loading/partial graph transfer, object modification, prototype definition, and more. The JSPON specification can be found at www.jspon.org.
You can start the browser by opening browser.html in your browser. There is sample JSPON data that comes with the browser. When the browser asks what object you want to open, enter "SampleData". You can then see some examples of the expressive capabilities of the JSPON. There are further examples that can be access on the jspon website. Using the JSPON browser, you can load the object http://www.jspon.org/browser.html?id=dyna%2F100788 (or https://xucia.com/browser.html?id=dyna%2F100788 if you are using Adblock/filterset G which blocks jspon for some reason). The following properties of the root object from the example url illustrate some of the major aspects of JSPON:
BEGIN LICENSE BLOCK Version: MPL 1.1/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. END LICENSE BLOCK