Ruby scripting in java

Java Scripting and JRuby Examples

Author: Martin Kuba

  • Installation of JRuby
  • JRuby Example 1 — calling Ruby code
  • Example 2 — Ruby classes in Java
  • JRuby in TomCat

The new JDK 6.0 has a new API for scripting languages, which seems to be a good idea. I decided this is a good opportunity to learn the Ruby language 🙂 But I haven’t found a simple example of it using Google, so here it is.

Installation of JRuby

export CLASSPATH=. for i in *.jar ; do CLASSPATH=$CLASSPATH:$i; done

JRuby Example 1 — calling Ruby code

Here is a simple Java code that executes a Ruby script defining some functions and the executing them:

package cz.cesnet.meta.jruby; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.ScriptContext; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; public class JRubyExample1 < public static void main(String[] args) throws ScriptException, FileNotFoundException < //list all available scripting engines listScriptingEngines(); //get jruby engine ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby"); //process a ruby file jruby.eval(new BufferedReader(new FileReader("myruby.rb"))); //call a method defined in the ruby source jruby.put("number", 6); jruby.put("title", "My Swing App"); long fact = (Long) jruby.eval("showFactInWindow($title,$number)"); System.out.println("fact: " + fact); jruby.eval("$myglobalvar = fact($number)"); long myglob = (Long) jruby.getBindings(ScriptContext.ENGINE_SCOPE).get("myglobalvar"); System.out.println("myglob: " + myglob); > public static void listScriptingEngines() < ScriptEngineManager mgr = new ScriptEngineManager(); for (ScriptEngineFactory factory : mgr.getEngineFactories()) < System.out.println("ScriptEngineFactory Info"); System.out.printf("\tScript Engine: %s (%s)\n", factory.getEngineName(), factory.getEngineVersion()); System.out.printf("\tLanguage: %s (%s)\n", factory.getLanguageName(), factory.getLanguageVersion()); for (String name : factory.getNames()) < System.out.printf("\tEngine Alias: %s\n", name); >> > >

And here is the Ruby code in file myruby.rb:

def fact(n) if n==0 return 1 else return n*fact(n-1) end end class CloseListener include java.awt.event.ActionListener def actionPerformed(event) puts "CloseListere.actionPerformed() called" java.lang.System.exit(0) end end def showFactInWindow(title,number) f = fact(number) frame = javax.swing.JFrame.new(title) frame.setLayout(java.awt.FlowLayout.new()) button = javax.swing.JButton.new("Close") button.addActionListener(CloseListener.new) frame.contentPane.add(javax.swing.JLabel.new(number.to_s+"! font-weight: bold;">fact(n) which computes the factorial of a given number. Then it defines a (Ruby) class CloseListener, which extend a (Java) class java.awt.event.ActionListener. And finaly it defines a function showFactInWindow, which builds a GUI window displaying a label and a close button, assigns the CloseListener class as a listener for the button action, and returns the value of n! :

Java JFrame window displayed from Ruby code

Please note that a Ruby and Java classes can be mixed together.

(To run the example save the codes above into files cz/cesnet/meta/jruby/JRubyExample1.java and myruby.rb, and compile them and run using

javac cz/cesnet/meta/jruby/JRubyExample1.java java cz.cesnet.meta.jruby.JRubyExample1

You can pass any Java object using the put("key",object) method on the ScriptingEngine class, the key becomes a global variable in Ruby, so you can access it using $key . The numerical value returned by showFactInWindow is Ruby's Fixnum clas, which is converted into java.lang.Long and returned by the eval() method.

Any additional global variable in the Ruby script can be obtained in Java by getBindings() , as is shown by getting the $myglobalvar RUby global variable.

Example 2 - Ruby classes in Java

In JRuby 0.9.8, it was not possible to override or add methods of Java classes in Ruby and call them in Java. However, in JRuby 1.0 it is possible. If you have read the previous version of this page, please note, that the syntax for extending Java interfaces has changed in JRuby 1.0 to use include instead of .

This is a Java interface MyJavaInterface.java :

package cz.cesnet.meta.jruby;

public interface MyJavaInterface String myMethod(Long num);
>

This is a Java class MyJavaClass.java :

package cz.cesnet.meta.jruby;

public class MyJavaClass implements MyJavaInterface public String myMethod(Long num) return "I am Java method, num font-weight: bold;">example2.rb:
#example2.rb class MyDerivedClass < Java::cz.cesnet.meta.jruby.MyJavaClass def myMethod(num) return "I am Ruby method, num="+num.to_s() end end class MyImplClass include Java::cz.cesnet.meta.jruby.MyJavaInterface def myMethod(num) return "I am Ruby method in interface impl, num="+num.to_s() end def mySecondMethod() return "I am an additonal Ruby method" end end
package cz.cesnet.meta.jruby; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.lang.reflect.Method; public class JRubyExample2 < public static void main(String[] args) throws ScriptException, FileNotFoundException < //get jruby engine ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby"); //process a ruby file jruby.eval(new BufferedReader(new FileReader("example2.rb"))); //get a Ruby class extended from Java class MyJavaClass mjc = (MyJavaClass) jruby.eval("MyDerivedClass.new"); String s = mjc.myMethod(2l); //WOW! the Ruby method is visible System.out.println("s: " + s); //get a Ruby class implementing a Java interface MyJavaInterface mji = (MyJavaInterface) jruby.eval("MyImplClass.new"); String s2 = mji.myMethod(3l); //WOW ! the Ruby method is visible System.out.println("s2: " + s2); //however the other methods are not visible :-( for (Method m : mji.getClass().getMethods()) < System.out.println("m.getName() c4">JRuby in TomCat 

A useful usage of JRuby in Java is in web applications, when you need to give your user the option to write some complex user-defined conditions. For example, I needed to allow users to specify conditions for other users to access a chat room, based on attributes of the other users provided by autentization system Shibboleth. So I implemented JRuby scripting in TomCat-deployed web application. I used TomCat 6.0, but 5.5 should work the same way. Here are my findings.

First the easy part. Just add the jar files needed for JRuby to WEB-INF/lib/ directory and it just works. Great ! Now your users can enter any Ruby script, and you can provide it with input data using global variables, execute it and read its output value, in a class called from a servlet. In the following example, I needed a boolean value as the output, so the usual Ruby rules for truth values are simulated:

public boolean evalRubyScript() < ScriptEngine jruby = engineManager.getEngineByName("jruby"); jruby.put("attrs", getWhatEverInputDataYouNeedToProvide()); try < Object retval = jruby.eval(script); if (retval instanceof Boolean) return ((Boolean) retval); return retval != null; >catch (ScriptException e) < throw new RuntimeException(e); >>

However, your users can type anything. Sooner or later, somebody will type something harmful, like java.lang.System.exit(1) or File.readlines('/etc/passwd') etc. You have to limit what the users can do. Fortunately, there is a security framework in Java, which is not enabled by default, by you can enable it by starting TomCat with the -security option:

$CATALINA_BASE/bin/catalina.sh start -security

That runs the JVM for TomCat with SecurityManager enabled. But alas, your web application most likely will not work with security enabled, as your code or the libraries you use now cannot read system properties, read files etc. So you have to allow them to do it. Edit the file $CATALINA_BASE/conf/catalina.prolicy and add the following code, where you replace mywebapp with the name of your web application:

//first allow everything for trusted libraries, add you own grant codeBase "jar:file:$/webapps/mywebapp/WEB-INF/lib/stripes.jar!/-" < permission java.security.AllPermission; >; grant codeBase "jar:file:$/webapps/mywebapp/WEB-INF/lib/log4j-1.2.13.jar!/-" < permission java.security.AllPermission; >; //JSP pages don't compile without this grant codeBase "file:$/work/Catalina/localhost/mywebapp/" < permission java.lang.RuntimePermission "defineClassInPackage.org.apache.jasper.runtime"; >; //if you need to read or write temporary file, use this grant codeBase "file:$/webapps/mywebapp/WEB-INF/classes/-" < permission java.io.FilePermission "$/file.ser", "read,write" ; >; // and now, allow only the basic things, as this applies to all code in your webapp including JRuby grant codeBase "file:$/webapps/mywebapp/-" < permission java.util.PropertyPermission "*", "read"; permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.RuntimePermission "createClassLoader"; permission java.lang.RuntimePermission "defineClassInPackage.java.lang"; permission java.lang.RuntimePermission "getenv.*"; permission java.util.PropertyPermission "*", "read,write"; permission java.io.FilePermission "$/.jruby", "read" ; permission java.io.FilePermission "file:$/webapps/mywebapp/WEB-INF/lib/jruby.jar!/-", "read" ; >;

Your webapp will probably not work even after you added this code to the policy file, as your code may need permissions to do other things to work. I have found that the easiest way how to find what's missing is to run TomCat with security debuging enabled:

$ rm logs/* $ CATALINA_OPTS=-Djava.security.debug=access,failure bin/catalina.sh run -security . reproduce the problem by accessing the webapp . $ bin/catalina.sh run stop $ fgrep -v 'access allowed' logs/catalina.out
access: access denied (java.lang.RuntimePermission accessClassInPackage.sun.misc) java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1206) at java.security.AccessControlContext.checkPermission(AccessControlContext.java:313) . access: domain that failed ProtectionDomain (file:/home/joe/tomcat-6.0.13/webapps/mywebapp/WEB-INF/lib/somelib.jar ^lt;no signer certificates>)

That means, that the code in somelib.jar need the RuntimePermission to run, so you have to add it to the catalina.policy file. Then repeat the steps untill you web application runs without problems.

Now the users cannot do dangerous things. If they try to type java.lang.System.exit(1) in the JRuby code, the VM will not exit, instead they will get a security exception:

java.security.AccessControlException: access denied (java.lang.RuntimePermission exitVM.1) at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323) at java.security.AccessController.checkPermission(AccessController.java:546) at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)

Источник

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

JRuby, an implementation of Ruby on the JVM

License

Unknown, Unknown licenses found

Licenses found

jruby/jruby

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Expanding on an older method of putting call linenums in right place

Git stats

Files

Failed to load latest commit information.

README.md

JRuby - an implementation of the Ruby language on the JVM

JRuby is an implementation of the Ruby language using the JVM.

It aims to be a complete, correct and fast implementation of Ruby, at the same time as providing powerful new features such as concurrency without a global-interpreter-lock, true parallelism, and tight integration to the Java language to allow you to use Java classes in your Ruby program and to allow JRuby to be embedded into a Java application.

You can use JRuby simply as a faster version of Ruby, you can use it to run Ruby on the JVM and access powerful JVM libraries such as highly tuned concurrency primitives, you can use it to embed Ruby as a scripting language in your Java program, or many other possibilities.

We're a welcoming community - you can talk to us on #jruby on Libera. There are core team members in the EU and US time zones.

Visit the JRuby website and the JRuby wiki for more information.

To run JRuby you will need a JRE (the Java VM runtime environment) version 8 or higher.

Your operating system may provide a JRE and JRuby in a package manager, but you may find that this version is very old.

An alternative is to use one of the Ruby version managers.

For rbenv you will need the ruby-build plugin. You may find that your system package manager can provide these. To see which versions of JRuby are available you should run:

Note: if you do not regularly git update rbenv this list of versions may be out of date.

We recommend always selecting the latest version of JRuby from the list. You can install that particular version (9.2.13.0 is just for illustration):

You can also download packages from the JRuby website that you can unpack and run in place.

See BUILDING for information about prerequisites, how to compile JRuby from source and how to test it.

Stefan Matthias Aust, Anders Bengtsson, Geert Bevin, Ola Bini, Piergiuliano Bossi, Johannes Brodwall, Rocky Burt, Paul Butcher, Benoit Cerrina, Wyss Clemens, David Corbin, Benoit Daloze, Thomas E Enebo, Robert Feldt, Chad Fowler, Russ Freeman, Joey Gibson, Kiel Hodges, Xandy Johnson, Kelvin Liu, Kevin Menard, Alan Moore, Akinori Musha, Charles Nutter, Takashi Okamoto, Jan Arne Petersen, Tobias Reif, David Saff, Subramanya Sastry, Chris Seaton, Nick Sieger, Ed Sinjiashvili, Vladimir Sizikov, Daiki Ueno, Matthias Veit, Jason Voegele, Sergey Yevtushenko, Robert Yokota, and many gracious contributors from the community.

JRuby uses code generously shared by the creator of the Ruby language, Yukihiro Matsumoto matz@netlab.co.jp.

Project Contact: Thomas E Enebo tom.enebo@gmail.com

JRuby is licensed under a tri EPL/GPL/LGPL license. You can use it, redistribute it and/or modify it under the terms of the:

Eclipse Public License version 2.0 OR GNU General Public License version 2 OR GNU Lesser General Public License version 2.1

Some components have other licenses and copyright. See the COPYING file for more specifics.

Источник

Читайте также:  Java вывести имя переменной
Оцените статью