锁定老帖子 主题:Java和Jython的结合
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-04-30
最后修改:2010-04-30
最近重新设计一个程序,这个程序有一个功能是监视其他进程的内存使用情况,并且在满足一定条件的时候促发警告给用户。一般的条件是,内存超过某个阀值。可是如果希望这个条件可以复杂些,譬如超过2G时并且持续增长2天,就告警;或者如果小于1G,并且在10天以内总体趋势是增长的,也告警。总之,这个条件可以支持很复杂的条件,并且需要动态修改(主模块不重启的情况下),所以我的选择是结合Java和Jython。其实Java上面可以选用JRuby, Scala, Groovy或者Javascript,选择Jython的理由有以下几点: [1] 单位有一些项目已经使用了Python; [2] Scala和Groovy会的人太少,太前卫了; [3] Javascript很好,不过考虑到也许今后所有Java的程序扩展都会使用一种脚本语言,明显Javascript系统的交互能力太差。譬如链接数据库,处理文件啊 [4] Ruby是我太难割舍的,因为我喜欢Ruby,其实Ruby企业版已经比较稳定了,而且JRuby也有官方的支持。只不过,单位中已经有项目使用了Python,如果冒然的引入Ruby,势必会增加系统的复杂度。 [5] 即使有JPerl,JTcl,我也不会选用。原因很简单,我需要更好的模块化面向对象的脚本,面向对象不是银弹,可是大家都很熟悉面向对象了,而且实践证明面向对象鼓励大家写出好维护的代码(结合设计模式)。 [6] C++可以和Python集成(Boost.Python)(当然了,Lua更方便与C++集成,可是Lua会的人也太少)。 [7] 可以用Java写,然后用反射,可以做。不过我觉得脚本更好,省掉了编译的过程。(因为在现场,不能保证你的Unix上面装了JDK,只能保证一定有JRE)。 [8] 使用的是Java5,不是Java6,所以JSR233(好像是这个吧)不是直接可以用的。
因为这个项目是单位的项目,所以我把我做得Prestudy的小程序拿出来和大家共享一下解决方案。
我总喜欢用计算器这样的小例子来做PreStudy,呵呵。
首先设计一个计算器的接口ICalc(用Java写的), 如下:
package com.gmail.at.ankyhe.calc; public interface ICalc { void pushData(int[] data); int getResult(); } package com.gmail.at.ankyhe.calc; public interface ICalc { void pushData(int[] data); int getResult(); }
很显然,这个接口里面有两个函数。第一个函数是pushData,主模块调用这个函数把数据送给具体的Calc类(这个用Jython实现),然后通过getResult()得到结果,思路很简单。为了能够动态的发现更新的文件,我们把送数据给Calc并且从Calc得到结果的任务放到一个独立的线程里面,如下:
package com.gmail.at.ankyhe.calc; import java.util.Arrays; import java.util.Date; import java.util.Random; import java.util.concurrent.TimeUnit; public class CalcTask implements Runnable { public CalcTask(ICalc aCalc) { calc = aCalc; rand = new Random((new Date()).getTime()); arr = new int[3]; } public ICalc setCalc(ICalc aCalc) { ICalc tmp = calc; synchronized(this) { calc = aCalc; } return tmp; } public void run() { while (true) { for(int i = 0 ; i < arr.length; ++i) { arr[i] = rand.nextInt() % 50; } int rst = 0; synchronized (this) { calc.pushData(arr); rst = calc.getResult(); } System.out.println(String.format("Input: %s --- Output: %d", Arrays.toString(arr), rst)); try { TimeUnit.SECONDS.sleep(50); } catch (InterruptedException ex) { ex.printStackTrace(); } } } private ICalc calc; private Random rand; private int[] arr; }
然后主线程里面负责扫描Jython脚本和加载它,如下:
package com.gmail.at.ankyhe.calc; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.python.util.PythonInterpreter; public class Main { public static ICalc getCalc() { Reader file = null; file = null; try { file = new FileReader(filename); } catch(FileNotFoundException ex) { System.out.println("Can't find the file."); System.exit(1); } try { Reader in = new BufferedReader(file); PythonInterpreter interpreter = new PythonInterpreter(); org.python.core.PyObject code = interpreter.compile(in); interpreter.exec(code); ICalc calc = (ICalc)interpreter.get("calc", ICalc.class); return calc; } finally { try { file.close(); } catch(IOException ex) { System.out.print("Fail to close file"); System.exit(1); } } } public static long getLastModified() { long rst = 0L; File f = null; try { f = new File(filename); rst = f.lastModified(); } catch(NullPointerException ex) { ex.printStackTrace(); rst = -1; } return rst; } public static void main(String[] args) throws InterruptedException { // initialization if (args.length == 0) { System.out.println("Usage java -jar calc.jar <scriptfile>."); System.exit(1); } filename = args[0]; ICalc calc = null; if (lastmodified == 0) { lastmodified = getLastModified(); if (lastmodified == -1) { System.out .println(String.format("The file %s doesn't exit.", filename)); } calc = getCalc(); } if(calc == null) { System.out.print(String.format("The script file has no valid calc.", filename)); System.exit(2); } // launch calc task CalcTask ct = new CalcTask(calc); ExecutorService es = Executors.newCachedThreadPool(); es.execute(ct); es.shutdown(); // main thread check the new script every 10 seconds while(true) { long modified = getLastModified(); if (modified <= lastmodified) { // do nothing } else { lastmodified = getLastModified(); ICalc newCalc = getCalc(); if (newCalc == null) { System.out.print(String.format("The new script file has no valid calc.", filename)); } else { System.out.println("Ready to use new script file."); ct.setCalc(newCalc); System.out.println("Use new script file."); } } TimeUnit.SECONDS.sleep(10); } } public static String filename = null; public static long lastmodified = 0; }
核心的加载代码就是下面这一段:
PythonInterpreter interpreter = new PythonInterpreter(); org.python.core.PyObject code = interpreter.compile(in); interpreter.exec(code); ICalc calc = (ICalc)interpreter.get("calc", ICalc.class);
注意,在Jython脚本中的calc一定是一个ICalc子类的对象,否则上面最后一句会抛出异常。Jython代码如下:
import sys # add sys path def addSysPath(): sys.path.append("/Users/AnkyHe/proj/java/CalcPlugin/icalc.jar") addSysPath() from com.gmail.at.ankyhe.calc import ICalc class MyCalc(ICalc): def __init__(self): pass def pushData(self, arr): self.arr = arr def getResult(self): return sum(self.arr) #return min(self.arr) calc = MyCalc() ''' if __name__ == '__main__': mycalc = MyCalc() arr = [1, -100, 20] mycalc.pushData(arr) if sum(arr) == mycalc.getResult(): print('true') else: print('false') print(mycalc.getResult()) ''' sys.path.append是把接口ICalc的jar文件加入到Jython的使用环境中,这样才可以import ICalc。注意最后的if __name__ == '__main__', 这里体现了用Jython的一个优点,可以通过Jython做测试。因为这只是一个Presutdy,主要任务是验证一下这个流程,所以可能有一些错误处理没有写得很全面。源代码和可执行文件(我在Mac上编译运行通过了)如附件,执行的时候请用: java -cp ./icalc.jar -jar calcplugin.jar script/mycalc.py 编译的时候使用把icalc.jar和jython.jar加入到classpath中。 然后你修改mycalc.py,可以看到输出结果的改变。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 3497 次