Platypus.js

JavaScript RAD platform


JavaScript RAD platform for Java EE

  • Platypus.js is built on top of Java EE.
  • Platypus.js supports both non-blocking and blocking IO models.
  • Unlike Ringojs, heart of Platypus.js is modern JavaScript engine Nashorn.
  • Unlike Node.js and Ringojs, Platypus.js uses AMD modules loader both on client and on server and doesn't push you to use such things as browserify.
  • Unlike Node.js, Platypus.js uses standard thread pool approach for parallel execution of code, thus distributing load across processor cores evenly, even if load oscillates.
  • Unlike Node.js, Platypus.js avoids inter-process or inter-Web Workers communication overheand while parallel execution of code.
  • Single threaded nature of JavaScript programming language is preserved through deep copy with reference graphs trick without expensive synchronization techniques.
  • Platypus.js provides comprehenshive set of libraries out-of-the-box to build enterprise-grade applications quickly and easily. For example, HTLML5 CSS3 widgets for browser and Swing widgets for Java SE client.
  • Also, there is community project Platypus designer. It provides a complete IDE to build applications with Platypus.js.

Web and desktop apps

Module system

Role based security

Parallel JavaScript

Universal UI forms and widgets

Data on your fingertips

Develop Web and even desktop applications using JavaScript.

Sometimes a browser is just not enough for your application as a client.
Maybe you do not need an application server at all.
In this case use the desktop Java SE client to run the same UI forms as a HTML5 browser.

Module system

Modules of Platypus.js are JavaScript modules desinged as AMD modules or as Global modules

  • AMD modules loader both on server and on client
  • Multiple JavaScript engines in single Java process with no inter-process communication overhead
  • Automatic dependencies resolving of global modules
  • Java EE role based security system
  • Modules can have file like names or short names depending on wich version of define function used
Modules can run on a client as well as on a server.
The code snippet below shows a simple module example
                                    
/**
 * To assign a short name to this module, use three arguments define.
 */
define('documents', ['logger', 'orm'], function(Logger, Orm, ModuleName){
    function Documents() {
        var model = Orm.loadModel(ModuleName);
        this.logDocuments = function(categoryId) {
            model.documents.params.catId = categoryId;
            model.requery(function(){
                model.documents.forEach(function(doc) {
                    Logger.info("Document name: " + doc.name);
                });
            });
        };
    }
    return Documents;
});
                                    
                                

Security

Security support comes out-of-the-box in Platypus.js. Every resource like module or Sql query can be protected on the base of a currently logged-in user's roles. Platypus.js security subsystem is based on the Java EE security model and provides a very configurable mechanism for restricting access.
Various user information storages are supported. Use database user information storage or any external provider like Microsoft Active Directory or an OpenLDAP server.

Security annotations in JavaScript modules

                                        
    /**
     * In general, Platypus module starts with the JsDoc annotation
     * @author athorName
     * To allow usage from the client:
     * @public 
     * To set user roles allowed to use this module:
     * @rolesAllowed admin, developer
     * To assign a short name to this module, use three arguments define
     */
    define('GreatnessChecker', ['security'], function(Security){
        function GreatnessChecker() {
        
            var self = this;

            /**
             * You can define allowed roles for each function
             * @rolesAllowed admin
             */
            self.amIGreat = function (onSuccess, onFailure) {
                Logger.info('Looking if the user is great...');
                Security.principal(function(aPrincipal){
                    if(aPrincipal.hasRole('developer'))){
                        Logger.info('Ah, the user is Great!');
                        onSuccess('Ah, you are Great!');
                    } else {
                        Logger.info('No, the user is not Great.');
                        onSuccess('No, you are not Great.');
                    }
                }, onFailure);
            }
        }
        return GreatnessChecker;
    });
                                        
                                    

Security annotations in Sql queries

                                        
    /**
     * @author authorName
     * @name myQuery
     * To allow usage from the client side:
     * @public
     * @rolesAllowed maintainer, admin
     * @rolesAllowedRead guestUser
     * @rolesAllowedWrite ghostUser
     */
     Select * From someTable t1
     Where t1.someColumn = :myParam
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
                                        
                                    

Parallel JavaScript and IO models

Platypus.js server JavaScript works in multi-threaded environment with a few threads (in general cores + 1 threads if async IO is used).
Platypus.js gives you a chance to choose the IO model for the specific situation and allowes to use both AsyncIO or Blocking IO models, providing performance that is good enough for the most types of applications.

Here is an example of parallel executed modules

                                    
    /**
     * Here is session statelessl module, serving user's request.
     * The following annotation is needed to create stateless module. 
     * @stateless
     */
    define(function (){
        function Worker(){
            var self = this;

            this.calc = function (aAngle) {
              return Math.tan(aAngle);// One of parallel operations here
            }
        }
        return Worker;
    });

    /*
     * Here is session statefull module, serving user's request and
     * performing calculations in 'Worker' module.
     * Session statefull modules are created by default without any annotations.
     */
    define(['rpc'], function(Lpc){
        function Processor(){
            var self = this;
            this.process = function(aAngles, aOnSuccess){
                var results = [];
                var w = new Lpc.Proxy('Worker');
                var calced = 0;
                aAngles.forEach(function(aAngle){
                    w.calc(aAngle, function(aTan){// Call to LPC stub. Actual calc
                                                  // function will be called by 
                                                  // Platypus.js runtime probably in
                                                  // several concurrent threads.
                        results.push(aTan);
                        if(results.length === aAngles.length){
                            aOnSuccess(results);
                        }
                    });
                });
            }
        }
        return Processor;
    });
                                    
                                

Here are examples of asynchronous data queries

With loading data and replacing the data of the entity:

                                    
    // The first param used as the succesfull callback function and the second as the failure callback
    model.myDataEntity.params.keyField = 'keyValue';
    model.myDataEntity.requery(function () {
        // Data in 'model.myDataEntity' have been replaced by just retrieved data.
        processData();
    },function(){
        Logger.warning("Couldn't load data!");
    });

    someMoreWork(); // It will be executed immidiatly after calling the above requery function without waiting for it's result
    // There are a lot of functions that could work asynchronously. To learn more have a look at the documentation.
                                    
                                

Query execution without replacing the data of entity:

                                    
    // The first param used as parameters container.
    model.myDataEntity.query({keyField: 'keyValue'}, function (loadedData) {
        // Data in 'model.myDataEntity' remain unchanged.
        // 'loadedData' contain new just retrieved data.
        processData(loadedData);
    },function(){
        Logger.warning("Couldn't load data!");
    });

    someMoreWork(); // It will be executed immidiatly after calling the above requery function without waiting for it's result
    // There are a lot of functions that could work asynchronously. To learn more have a look at the documentation.
                                    
                                

Universal UI forms

A Platypus.js form is a kind of a module with UI layout definition in a Xml file.
Single-page web application or Java SE desktop application share the same forms. A library of widgets includes a bunch of simple widgets, panels and menus as well as smart model widgets which are bound to the model's data. With visual form editor you can create your application user interface without any coding and knowledge about HTML and CSS. Have a look on UI Demo. In general, Platypus.js forms are MVVM modules. In some cases you might want to return some templated page from server and you are free to do so using server modules.

Data on your fingertips

Platypus.js provides you with access to various relational and noSQL databases. In general it should be JDBC compliant database, but there is a MongoDB client for Platypus.js also.

Platypus.js has data model, that provides access to data in your application in an Orm manner if relational database is used.

Orm of Platypus.js has a following features:

  • Direct using of Sql. You can say exactly what you want from a database: how query, insert or update your data.
  • Orm transforms relational data into JavaScript objects automatically without any mapping configuration, annotations, etc.
  • Orm configuration is only a list of queries names to be processed during application runtime.
  • Orm generates data change commands on the base of Select queries automatically.
  • You can write database specific queries if you want.
  • Orm also transforms foreign keys information into scalar references and collections in interlinked JavaScript objects automatically.

For example you have a Sql query

                                    
    /**
     * @author greatMan
     * @name testQuery
     * This annotation tells Platypus.js, that this query's data can be requested by client through application server.
     * @public
     * The following annotation tells Platypus.js what table could be modified,
     * while applying a changelog to the database:
     * @writable employees
     */
     Select t1.employee_id, t1.employee_name employeeName, t2.city_name
     From employees t1
     Inner join cities t2 On t1.city_id = t2.city_id
                                    
                                

This query is connected with data model. And it becames a new JavaScript array. You are free to change properties of it's elements. These changes will be recorded as a change log wich later may be saved to a database. Elements deletion and inserting are also recorded in a change log.

                                    
    /**
     * @author OneGreatMan
     * @public
     */
    define(['logger', 'orm'], function(Logger, Orm, ModuleName){ // ModuleName is extra parameter, added by Platypus.js runtime
        function EmployeeUtils() {
            var self = this, model = Orm.loadModel(ModuleName); // This line was automaticly generated on module creation

            this.processEmployees = function () {
                // Model's data sets are regular javascript arrays
                model.testQuery.forEach(function(aItem){
                    if(!aItem.city_name)// Fields names are used as properties names if aliases are absent.
                        aItem.employeeName = 'Homeless employee :)'; // Fields aliases are used as properties names if they exist.
                });
                // It will save all data changes at exactly same sequence as they were made.
                model.save(function(){
                    Logger.info('Data have been saved successfully');
                });
            };
        }
        return EmployeeUtils;
    });
                                    
                                

Netbeans IDE plugins for Platypus.js projects

It's all that you need to whole developing process. Read more...