Firstly, I'll start with a very simple servlet. It just shows simple messages after executing a hello-world script written in Ruby. Tediously simple, but important to make sure that scripting APIs work on a servlet container. Because Servlet API and its web application require a specific directory structure, and adopt their own classloading mechanism, we need to fit scripting API in to a servlet based web application. Unless, some of programmers might experience hard time for the frist servlet code in action.
Before start writing the code, we must set up two jar archives, jruby.jar and jruby-engine.jar, to the right place. It is WEB-INF/lib. Most programmers use some sort of IDE and probably know about how to set up these two archives in a web application. I'm a NetBeans user, so I clicked on Libraries > Add JAR/Folder menu of a web project, and added them.
Here are the simple servlet and Ruby script:
package mountainash;Lines colored red have scripting APIs mixed in to this servlet. In a init() method, the servlet gets JRuby engine's instance, which means that this servlet has only one JRuby runtime to process multiple requests rushed to this servlet all at once. Also, only one instance of JRuby engine takes care of multiple http requests concurrently. This is why this servlet doesn't use engine.put() or engine.setWriter() methods not to be suffered from a race condition. Instead, this servlet creates SimpleScriptContext type instance for each http request to share objects with Ruby script.
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SimpleServlet extends HttpServlet {
private ScriptEngine engine;
@Override
public void init() {
ScriptEngineManager manager = new ScriptEngineManager();
engine = manager.getEngineByName("jruby");
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
SimpleScriptContext scriptContext = new SimpleScriptContext();
scriptContext.setWriter(out);
try {
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet SimpleServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Servlet SimpleServlet at " + request.getContextPath() + "</h3>");
out.println("<pre>");
ClassLoader loader = getClass().getClassLoader();
Reader reader =
new InputStreamReader(loader.getResourceAsStream("ruby/simple.rb"), "UTF-8");
engine.eval(reader, scriptContext);
out.println("</pre>");
out.println("</body>");
out.println("</html>");
} catch (ScriptException ex) {
Logger.getLogger(SimpleServlet.class.getName()).log(Level.SEVERE, null, ex);
} finally {
out.close();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "JRuby Engine Test: very simple";
}
}
--------------------
# simple.rb
puts "Hello World from Ruby over JRuby engine"
puts "こんにちは世界"
Where do I put Ruby scripts? This question might come up since everything should be in Java's web application directory structure. The way of getting scripts depends on where are they. We can take scripts in the servlet either specifing the file path relative to the web application's context root or reading the file as a resource by using a class loader. This servlet uses the latter one, so Ruby scripts must be in the WEB-INF/lib or WEB-INF/classes directory, or subdirectories of WEB-INF/classes, which are available for web application's class loader to load resources. Again, I'm a NetBeans user, so I created the folder whose name is ruby under the Source Packages folder and put the script there. Then, NetBeans copied it to build/web/WEB-INF/classes/ruby directory automatically, consequently, the script became loadable.
Mountainash project's directory hierarcy
+ Mountainash
+ WEB-INF
+ src
+ mountainash
- SimpleServlet.java
+ ruby
- simple.rb
+ lib
- jruby.jar
- jruby-engine.jar
+ classes -+
+ mountainash | automatically created
- SimpleServlet.class | directories and files
+ ruby |
- simple.rb -+
When this servlet gets run successfully, we get the following output:
<html>
<head>
<title>Servlet SimpleServlet</title>
</head>
<body>
<h3>Servlet SimpleServlet at /Mountainash</h3>
<pre>
Hello World from Ruby over JRuby engine
こんにちは世界
</pre>
</body>
</html>
3 comments:
Excellent example Servlet - also works very well with Jython: just change:
engine = manager.getEngineByName("python");
And other directory names etc to 'python' from 'ruby'.
Domo arrigato !
Additionally: I found that I had to add in an explicit 'reader.close()' to the Servlet; otherwise any changes to the (Python in my case) script weren't picked up.
Like this:
engine.eval(reader, scriptContext);
reader.close();
Thanks again.
Post a Comment