📜 ⬆️ ⬇️

Node.js vs Java + Rhino + Jetty + FreeMarker


Although Node.js has acquired many modules since its inception, it is still significantly inferior in its capabilities to a powerful set of Java libraries . So why not use the potential of Java to develop web-applications in JavaScript? Let's see how you can build a convenient JavaScript MVC framework in Java.

Mozilla rhino


First of all, let's start with rhinos. To compile / interpret JavaScript, we will use the Mozilla Rhino engine, which provides excellent integration of ECMAScript code into Java applications. Starting with J2SE 6, Rhino is included in the JRE as part of the Java Scripting API, however, the version in the JRE is significantly outdated and, moreover, with some unpleasant implementation features from Sun, so it’s better to use a fresh build .

First of all, helloworld.js :
 print('Hey you!'); 

Assuming that the Rhino libraries are unpacked in ./lib , we run the example as follows:
 java -Djava.ext.dirs=./lib org.mozilla.javascript.tools.shell.Main helloworld.js 


By the way, the bundle includes a debugger with a good UI, it starts like this:
 java -Djava.ext.dirs=./lib org.mozilla.javascript.tools.debugger.Main helloworld.js 


')
Rhino, in addition to the standard ECMAScript objects, includes in the global context a number of functions that facilitate the connection of JavaScript with Java. Yes, if you have not yet understood, it will be possible to work transparently with Java code from JavaScript code. There is a global Packages object for working with packages. For example, you can create an instance of java.io.File :
 var file = new Packages.java.io.File('filename'); 

However, there are also global objects java , com , org , edu and net , so the code can be reduced to the following:
 var file = new java.io.File('filename'); 

For import, you can use this pattern:
 var File = java.io.File; //... var file = new File('filename'); 

But it’s still more convenient:
 importClass(java.io.File); //... var file = new File('filename'); 

Or so:
 importPackage(java.io); //... var file = new File('filename'); 

Rhino allows you to implement Java interfaces in a convenient way for a JS programmer
 var runnable = new java.lang.Runnable({run: function() { print("I'm running!"); }}); new java.lang.Thread(runnable).start(); 

By the way, note that java.lang not imported into the global context to avoid conflicts with the built-in ECMAScript types.

And the latest versions of Rhino include a full implementation of CommonJS , which can be enabled in Rhino Shell with the help of switch -require .

If we have a module ./modules/math.js :
 exports.sum = function(a, b) { return a + b; } 

You can use it like this:
 var math = require('math'); print(math.sum(2, 4)); 

This code starts like this:
 java -Djava.ext.dirs=./lib org.mozilla.javascript.tools.shell.Main -require -modules ./modules test.js 


Jetty


Take Jetty as the basis for the HTTP server. Jetty is a container servlet, and at the same time flexible in setting up a web server with support for SPDY, WebSocket, OSGi, JMX, JNDI and JAAS. Download the distribution kit here .

The simplest code that runs Jetty:
 importPackage(org.eclipse.jetty.server); var server = new Server(8888); //  8888 server.start(); server.join(); //   Jetty 

JARs from the Jetty distribution should also be placed in ./lib , in the future we will run everything like this:
 java -Djava.ext.dirs=./lib org.mozilla.javascript.tools.shell.Main -require -modules ./modules server.js 

Yes that's all.

This server issues HTTP 404 on any request. Let's turn it into a simple file server.
 importPackage(org.eclipse.jetty.server); importPackage(org.eclipse.jetty.server.handler); var resourceHandler = new ResourceHandler(); resourceHandler.setDirectoriesListed(true); //       resourceHandler.setResourceBase('web'); //    ./web resourceHandler.setWelcomeFiles(['index.html']); //       index.html var server = new Server(8888); server.setHandler(resourceHandler); server.start(); server.join(); 

Now try to create a servlet.
 importPackage(org.eclipse.jetty.server); importPackage(org.eclipse.jetty.server.handler); importPackage(org.eclipse.jetty.servlet); importPackage(javax.servlet.http); var contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); contextHandler.setContextPath('/'); contextHandler.addServlet( new ServletHolder(new HttpServlet({ //      ,     doGet: function(request, response) { response.setContentType('text/plain'); response.getWriter().println('Yes, it works!'); } })), '/test' ); var server = new Server(8888); server.setHandler(contextHandler); server.start(); server.join(); 

Our servlet is available at localhost:8888/test localhost:8888/test . As one more example, let's design as a servlet module, which generates a picture with text on the fly.
 importPackage(java.awt.image); importClass(java.awt.Color); importClass(javax.imageio.ImageIO); //    J2SE 6 var width = 400, height = 400; exports.doGet = function(request, response) { var image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); var graphics = image.createGraphics(); var color = new Color(Math.random(), Math.random(), Math.random()); graphics.setColor(color); graphics.fillRect(0, 0, width, height); graphics.setColor(color.brighter()); graphics.drawString('On the fly!', 10, 20); response.setContentType('image/png'); var outputStream = response.getOutputStream(); ImageIO.write(image, 'png', outputStream); outputStream.close(); }; 

Place it in the ./modules folder as imageServlet.js and include it in the server code:
 contextHandler.addServlet( new ServletHolder(new HttpServlet(require('imageServlet'))), '/image.png' ); 

What's up with the DBMS? Let's see how to get a list of databases from MySQL.
 importPackage(java.sql); exports.doGet = function(request, response) { try { var connection = DriverManager.getConnection('jdbc:mysql://localhost/?', 'root', ''); try { var resultSet = connection .createStatement() .executeQuery('show databases;'); response.setContentType('text/html;charset=UTF-8'); var writer = response.getWriter(); writer.println('<h1>Databases</h1>'); while (resultSet.next()) { writer.println(resultSet.getString('Database') + '<br />'); } } catch(e) {} finally { resultSet.close(); } } catch(e) {} finally { if(connection) connection.close(); } }; 

This code will require MySQL Connector / J for JDBC.

Now there is the last component, the template engine.

Freemarker


FreeMarker is definitely the best template engine for Java, and not just for HTML and HTTP. You can write a separate article about its rich features, so let's get straight to the specifics.

Put in ./templates/template.ftl such a pattern:
 <html> <head> <title>${title}</title> </head> <body> <h1>${title}</h1> <#if message??> <pre>${message?html}</pre> <#else> <form method="post"> <textarea name="message"></textarea> <p><input type="submit" value="Post!"/></p> </form> </#if> </body> </html> 

The suffix ?html replaces those special characters in the substituted variable with escape sequences. This template will use the following servlet:
 importPackage(Packages.freemarker.template); importPackage(Packages.freemarker.ext.rhino); var configuration = new Configuration(); configuration.setObjectWrapper(new RhinoWrapper()); //   . var template = configuration.getTemplate('templates/template.ftl'); exports.doGet = function(request, response) { response.setContentType('text/html;charset=UTF-8'); template.process( {'title': 'Compose a message'}, response.getWriter() ); }; exports.doPost = function(request, response) { response.setContentType('text/html;charset=UTF-8'); template.process( { 'title': 'Message', 'message': request.getParameter('message') }, response.getWriter() ); }; 

Comparison with Node.js I nobly blame the reader. The full code for the example is available on GitHub .

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


All Articles