<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    ivaneeo's blog

    自由的力量,自由的生活。

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

    Java Scripting and JRuby Examples


    Author: Martin Kuba

    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

    Download and install JDK 6.0. You need version 6, as the scripting support was added in version 6. Then you need to put five files in your classpath:
    All you need to do is to add these files into the CLASSPATH. 
    (If you don't know how to do that, please read the Java tutorial first. Don't forget to include the current directory into the CLASSPATH. On Linux, you can do:
     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+"! = "+f.to_s))
    frame.contentPane.add(button)
    frame.defaultCloseOperation=javax.swing.WindowConstants::EXIT_ON_CLOSE
    frame.pack()
    frame.visible=true
    return f
    end
    

    The Ruby script defines a function 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="+num;
    }
    }

    This is a Ruby code 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
    

    This is the main code.

    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() = " + m.getName());
    }
    }
    }
    

    The output is

    s: I am Ruby method, num=2
    s2: I am Ruby method in interface impl, num=3
    m.getName() = myMethod
    m.getName() = hashCode
    m.getName() = equals
    m.getName() = toString
    m.getName() = isProxyClass
    m.getName() = getProxyClass
    m.getName() = getInvocationHandler
    m.getName() = newProxyInstance
    m.getName() = getClass
    m.getName() = wait
    m.getName() = wait
    m.getName() = wait
    m.getName() = notify
    m.getName() = notifyAll
    

    So you see, a Ruby method overriding a method in a Java class and a Ruby method implementing a method in a Java interface are visible in Java ! However additional methods are not visible in Java.

    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 replacemywebapp with the name of your web application:

    //first allow everything for trusted libraries, add you own
    grant codeBase "jar:file:${catalina.base}/webapps/mywebapp/WEB-INF/lib/stripes.jar!/-" {
    permission java.security.AllPermission;
    };
    grant codeBase "jar:file:${catalina.base}/webapps/mywebapp/WEB-INF/lib/log4j-1.2.13.jar!/-" {
    permission java.security.AllPermission;
    };
    //JSP pages don't compile without this
    grant codeBase "file:${catalina.base}/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:${catalina.base}/webapps/mywebapp/WEB-INF/classes/-" {
    permission java.io.FilePermission "${java.io.tmpdir}/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:${catalina.base}/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 "${user.home}/.jruby", "read" ;
    permission java.io.FilePermission "file:${catalina.base}/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
    
    This will filter out allowed accesses, so what remains are denied accesses:
    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)
    
    The web application is secured now.
    posted on 2011-04-22 13:31 ivaneeo 閱讀(466) 評論(0)  編輯  收藏 所屬分類: java魔力
    主站蜘蛛池模板: 亚洲精品狼友在线播放| 57pao国产成永久免费视频| 2020年亚洲天天爽天天噜| 亚洲日韩精品国产3区| igao激情在线视频免费| 国产精品无码免费专区午夜| 亚洲国产91精品无码专区| 久久综合九九亚洲一区| 亚洲.国产.欧美一区二区三区| 久久精品免费大片国产大片| 男女做羞羞的事视频免费观看无遮挡| 亚洲 无码 在线 专区| 亚洲国产美女在线观看 | 久久综合亚洲色一区二区三区| 一区二区亚洲精品精华液| 免费无码av片在线观看| 免费看香港一级毛片| 亚洲AV成人片色在线观看高潮| 亚洲国产精品无码久久98| 一级毛片免费观看不卡视频| 四只虎免费永久观看| 亚洲婷婷第一狠人综合精品| 免费看的成人yellow视频| 免费无码午夜福利片69| 18禁成年无码免费网站无遮挡 | 亚洲AV无码精品色午夜果冻不卡| 全免费a级毛片免费看| 免费人成视频在线观看视频 | 又粗又大又硬又爽的免费视频| 免费在线人人电影网| 亚洲电影一区二区三区| 成人毛片18女人毛片免费96| 亚洲欧洲日产v特级毛片| 日本一区二区三区日本免费| 最新亚洲卡一卡二卡三新区| 99re在线精品视频免费| 亚洲av无码乱码国产精品| 成人看的午夜免费毛片| 两个人的视频www免费| 最新国产AV无码专区亚洲| 一级做a爰黑人又硬又粗免费看51社区国产精品视 |