`

Java和Jython的结合

阅读更多

最近重新设计一个程序,这个程序有一个功能是监视其他进程的内存使用情况,并且在满足一定条件的时候促发警告给用户。一般的条件是,内存超过某个阀值。可是如果希望这个条件可以复杂些,譬如超过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,可以看到输出结果的改变。

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics