📜 ⬆️ ⬇️

Easy and dynamic business logic in JavaScript using Mozilla Rhino

image

Prehistory


I would like to start with the background. At the moment I am developing some kind of web application in Java, nothing unusual, but the document from the customer has a requirement: future application administrators should be able to fly the business logic code on the server on the fly. It seems to be nothing supernatural, it will be necessary to do loading of java-classes, I thought, until the other day I had an idea: “What if I can program business logic methods in JavaScript?”.
At that moment, the idea seemed very good to me, and I saw a number of advantages of this idea over the simple loading of java-classes:


But do not forget that big power is a big responsibility, therefore, following new opportunities, new questions appear that require a detailed answer.
In this article I would like to briefly describe the idea of ​​describing business logic in pure js, touch on the theoretical and practical parts, and also describe some nuances that may arise after this decision.

')

Choosing a JavaScript interpreter


For the engine that will not be interpreted for js, I didn’t have to go far; the first candidate is the Mozilla Rhino - js engine written entirely in Java.
The engine originates from 1997, born in the walls of Netscape under the name “Javagator”, but later, under mysterious circumstances, was renamed Rhino. Rhino was licensed by some large companies (including Sun) for their projects. Initially, the idea was to compile Java byte code based on JavaScript, but this approach had some problems:


In 1998, the default interpreter mode was added to the engine, i.e. JavaScript code is executed on the fly, bypassing the compilation in the byte code. This avoids problems with heavy compilation and memory leaks. Later this year, the engine opens the source code and is given to mozilla.org.
Download the library binaries and source code can be on of.sayte: link to the site . In the examples, rhino1.7 R4 will be used.
Rhino can be used in two ways: from the command line and directly by embedding a jar file in the project. We are now interested in the second method - we add the js.jar file to the project.

LiveConnect technology


One of the main ideas of Rhino is the ability to program a Java server using JavaScript code using LiveConnect technology. The technology makes it possible to access Java classes directly from js, without resorting to the help of some third-party code.
Here is a small example of accessing the File and System classes:
 var f = new java.io.File(“test.txt”); java.lang.System.out.println(f.exists()); 

LiveConnect gives us a lot of space for action, a lot is written about it in the office. documentation engine, but I will not now dwell on it, because We are interested in interpreting code from a non-trusted external source that should not have access to Java classes.

Using Rhino


As an example, we will develop a simple business logic module for the hypothetical project “Student Accounting and Assessment Services” for the university. The application should keep a list of all students and their assessments, as well as be able to perform some actions on them, based on the tasks.
Let's start with the basics of use. Rhino has 2 basic concepts - it is a context ( org.mozilla.javascript.Context class) and a sphere ( org.mozilla.javascript.Scriptable class). Context is an interpreter instance that binds to a single stream, therefore, interprets js in a single stream. A sphere is a so-called namespace in which we define all the variables of interest.
Example of creating context and scope:
 //   Context context = Context.enter(); //   Scriptable scope = context.initStandardObjects(); 

After we have created the context and scope, we need to restrict the interpreter access to Java classes. This is done using the setClassShutter method of the setClassShutter instance:
 //   LiveConnect  Java- context.setClassShutter(new ClassShutter() { @Override public boolean visibleToScripts(String fullClassName) { // ,      fullClassName  return false; } }); 

By default, Rhino uses LiveConnect technology, which gives access to java-classes directly from js. It gives a great opportunity to the trusted code, but we have another case - our server will interpret the potentially unsafe code.
It will be very unpleasant if the js-code of the following type gets into the interpreter:
 java.lang.System.exit(0); 

Therefore, we simply “shut up” LiveConnect and leave access only to those classes that we need. After we got the context and the scope, we have no choice but to interpret the js code:
 String script = “var mathStuff = Math.cos(Math.PI)”; c.evaluateString(scope, script, null, 1, null); 

That's all, after working with Rhino, we finish the work with the context and release the resources:
 Context.exit(); 


“Sandbox” for business logic


Now that we know how to start working with Rhino, we can proceed to defining the external business logic API in the form of several constant references to top-level modules:


 public class DatabaseModule { public DatabaseModule(){ } /*    ,       */ public String getStudent(int id){ return (id > 0) ? "  " : null; } /*    ,       */ public int getRating(String student){ return student.equals("  ") ? 5 : -1; } /*     ,          */ public void setRating(String student, int newRating){ System.out.println("setRating() student = "+student+", newRating = "+newRating); // Do something } } 

 public class NotificationModule { public NotificationModule(){ } /*     */ public void notifyStudent(String student, String message){ System.out.println("notifyStudent() student = "+student+", message = "+message); } /*    */ public void notifyCurator(String message){ System.out.println("notifyCurator() message = "+message); } } 

Next, we define constant links to modules in a previously defined area:
 //  DatabaseModule  js DatabaseModule database = new DatabaseModule(); Object wrappedDatabaseModule = Context.javaToJS(database, scope); ScriptableObject.putConstProperty(scope, "databaseModule", wrappedDatabaseModule); //  NotificationModule  js NotificationModule notification = new NotificationModule(); Object wrappedNotificationModule = Context.javaToJS(notification, scope); ScriptableObject.putConstProperty(scope, "notificationModule", wrappedNotificationModule); 


Programming business logic in javascript


Suppose that we faced a task: to select information about a student by his ID from the database, to get his grade, to calculate whether he has enough points to get admission to the exam and notify the curator and the student himself. In this case, the entire task will fall on the following js-code:
 var student = databaseModule.getStudent(1); var rating = databaseModule.getRating(student); var pass = rating >= 40; if(pass){ notificationModule.notifyCurator("Student "+student+" is admitted to the exam."); notificationModule.notifyStudent(student, "You admitted to the exam."); } else { var dif = 40 - rating; notificationModule.notifyCurator("Student "+student+" needs "+dif+" points to be admitted to the exam."); notificationModule.notifyStudent(student, "You need "+dif+" points to be admitted to the exam."); } 

All that remains is to configure the interpreter and pass js to it. I'll warn you right away: the engine painfully perceives the Cyrillic alphabet.

Conclusion


In conclusion, I will say that the idea of ​​the possibility of programming business logic on js is quite interesting, although not new. This approach provides some flexibility and ease of implementation.
The programmer, who is faced with the task of adding a new method, may not think about what technology stack is used in the server part, but simply prescribes what is required to be done, easily expand and supplement the functionality.
Following the possibilities that this approach carries, there are a number of questions that need to be taken care of before being implemented in the combat server:


Fortunately, all issues are completely solvable, the Rhino source code is open for modification.

useful links

Of project website: https://developer.mozilla.org/ru/docs/Rhino
Wikipedia article: http://ru.wikipedia.org/wiki/Rhino
Of documentation: https://developer.mozilla.org/en-US/docs/Rhino_documentation
API Reference (not office): http://tool.oschina.net/uploads/apidocs/rhino/
Sample sources on GitHub: https://github.com/andrew-medvedev/rhino-example

Source: https://habr.com/ru/post/229247/


All Articles