程式扎記: [ Jython 常見問題 ] Using Jython to call Java

標籤

2014年1月20日 星期一

[ Jython 常見問題 ] Using Jython to call Java

來源自 這裡
Preface:
I have been writing standalone Java applications lately, which are run from within a shell script, something like the snippet below. Calling Java standalones in this way is a fairly standard approach. However, being a Python fan(atic?), I would like to write these shell scripts using Python. This article describes how to call Java programs using Jython, which is a Java port of the Python programming language.
- Original shell script to invoke Java 
  1. #!/bin/bash  
  2. LIBDIR=/path/to/my/jar/repository  
  3. java -classpath $LIBDIR/commons-lang-2.1.jar:  $LIBDIR/com-mycompany-4.9.jar com.mycompany.mypackage.MyClass $*  
How-To:
In MyClass.java, I have a static main(String[] argv) method which gets invoked as a result of the call above. All it does is instantiate an instance of MyClass and pass some parameters to it, and then call a method on it that does the work. Here is an example, basically yet another implementation of HelloWorld.java, but it will do for illustrating the pattern.
- MyClass.java
  1. package com.mycompany.mypackage;  
  2.   
  3. import org.apache.commons.lang.StringUtils;  
  4.   
  5. public class MyClass {  
  6.   
  7.   private String name;  
  8.   
  9.   public MyClass() {  
  10.     super();  
  11.   }  
  12.   
  13.   public void setName(String name) {  
  14.     this.name = name;  
  15.   }  
  16.   
  17.   public String greet() {  
  18.     if (StringUtils.isEmpty(name)) {  
  19.       return "Hi there";  
  20.     } else {  
  21.       return "Hi " + name;  
  22.     }  
  23.   }  
  24.   
  25.   public static void main(String[] argv) {  
  26.     MyClass myclass = new MyClass();  
  27.     myclass.setName(argv[0]);  
  28.     System.out.println(myclass.greet());  
  29.   }  
  30. }  
Arguably, classes in the real world have more code in their main() methods, but the approach I take when I see that I have more than one object being invoked in my mainmethod, is to factor out the logic into private methods of that class, or into a Main.java class in that package if multiple classes are involved.

Anyway, the Jython script to call the main method of MyClass with a single string parameter that is passed in from the command line is shown below. Notice how the Jython script instantiates the MyClass object. The classpath is passed in to Jython using sys.path.append() calls. Resource files, such as .properties files or other XML configuration files need to be baked into the JAR file and should be accessible from within the Java code using getResourceAsStream().
  1. #!/usr/bin/env jython  
  2. import sys  
  3.   
  4. def setClassPath():  
  5.   libDir = "/path/to/my/jar/files/"  
  6.   classPaths = [  
  7.     "commons-lang-2.1.jar",  
  8.     "com-mycompany-4.9.jar"  
  9.   ]  
  10.   for classPath in classPaths:  
  11.     sys.path.append(libDir + classPath)  
  12.   
  13. def runJavaClass(name):  
  14.   from com.mycompany.mypackage import MyClass  
  15.   mc = MyClass()  
  16.   if (name != None):  
  17.     mc.setName(name)  
  18.   print mc.greet()  
  19.   
  20. def main():  
  21.   if (len(sys.argv) < 2):  
  22.     name = None  
  23.   else:  
  24.     name = sys.argv[1]  
  25.   setClassPath()  
  26.   runJavaClass(name)  
  27.   
  28. if __name__ == "__main__":  
  29.   main()  
As you can see, invoking Java programs from Jython leads to cleaner but more verbose scripts, but does seem to be overkill in our case. However, this approach can really help when you have to design more complicated scripts that deal with a large number of classes. One approach, and my preferred one, is to put this logic in Java in another abstraction layer. However, sometimes, this is not the most practical approach, perhaps because the rules are rather fluid, and you would like to be able tochange the rules without having to go through a big release cycle. In such cases, and depending on the complexity of the rules, I think I would be more productive using Python than Shell Scripts. Even if the rules were simple enough to be encoded within a Shell Script to start out with, it may still be more practical to use Jython, since the rules can get more complicated, and you may be faced with having to rewrite the script.

I don't think it is possible to set JVM parameters (such as minimum and maximum JVM sizesto Jython from within the Jython script, something that is possible using a Shell Script. Jython itself is a shell script and calls the org.python.util.jython class, so these parameters can be passed into the Java invocation in the jython shell script, although this does not seem to be very clean. Running the class through Jython also seems a little slower than running it through a Shell Script, but I haven't done any benchmarks to say this conclusively.

Test:
底下是我根據上面文章自己寫的 testing code. 首先是 Java 的部分:
- MyClass.java
  1. package demo;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4.   
  5. public class MyClass {  
  6.     private String name;  
  7.   
  8.       public MyClass() {  
  9.         super();  
  10.       }  
  11.   
  12.       public void setName(String name) {  
  13.         this.name = name;  
  14.       }  
  15.   
  16.       public Greet greet() {  
  17.         if (name==null || name.isEmpty()) {  
  18.             return new Greet("Hi there\n");  
  19.         } else {  
  20.             return new Greet(String.format("Hello %s\n", name));  
  21.         }  
  22.       }  
  23. }  
- Greet.java
  1. package demo;  
  2.   
  3. public class Greet {  
  4.     public String greet;  
  5.     public Greet(String g){this.greet=g;}  
  6.     public void hi(){System.out.printf("%s\n", greet);}  
  7. }  
接著將上面兩個 .java 編譯後, 壓縮到 Test.jar. 接著底下是 Jython script 來載入該 jar 檔並操作 MyClass 上面的方法:
- test.py
  1. import sys    
  2.   
  3. sys.path.append("Test.jar")  # 'Test.jar' 放在與此 .py 相同路徑下!  
  4. from demo import *  
  5. mc = MyClass()  
  6. mc.setName("John")  
  7. gt = mc.greet()  
  8. gt.hi()  
執行結果如下:
C:\Software\jython2.7> jython test.py
Hello John


沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!