iT邦幫忙

DAY 25
4

Javascript面面觀系列 第 25

Javascript面面觀:應用篇《Rhino》

Rhino是基於Java的Javascript引擎,大概也是Java用戶使用Javascript的不二之選。在部份的Java環境有整合,不必倚賴外部的Rhino就可以使用。

http://www.mozilla.org/rhino/
一些常見應用
* YUI Compressor
http://developer.yahoo.com/yui/compressor/

如果想要把Javascript檔案縮到最小,YUI Compressor應該是最好的解決方案之一。YUI Compressor可以去掉Javascript檔案中所有不需要的換行與註解,然後把函數的參數及變數改成簡短的名字,藉由這樣,可以大幅縮小Javascript程式的體積,並且使得程式不容易閱讀而達到保護的目的。

YUI Compressor內部使用Rhino來做Javascript的parser,把Javascript的字串轉成token來進一步處理。除了縮小體積,YUI Compressor也可以檢查Javascript是否有錯誤,只要加上額外的參數,他就會對程式碼的問題做回報。

* Flow Script
http://cocoon.apache.org/

Cocoon是Apache基金會底下的一個計畫,他是一個基於XML的網站應用軟體,不過他使用了Javascript做為流程控制語言,這個功能是使用Rhino實作的。

這部份的重點是continuation。關於什麼是continuation,wikipedia有一些解釋http://en.wikipedia.org/wiki/Continuation。利用continuation可以保存執行時的整個context環境的狀態,所以把他用serialization保存起來之後,就可以在之後接著執行下去。利用這個方法,Spring WebFlow以及Cocoon使用Rhino的Continuation物件,在需要輸出/輸入(也就是request/response)的時候進行Continuation來處理,處理完就回復狀態繼續執行。詳細的說明請參考:

* http://cocoon.apache.org/2.2/core-modules/core/2.2/1372\_1\_1.html
* http://cocoon.apache.org/2.2/core-modules/core/2.2/1373\_1\_1.html
* http://cocoon.apache.org/2.2/core-modules/core/2.2/1380\_1\_1.html

使用Rhino
一樣來試試看怎樣在Java程式中使用Rhino來內嵌Javascript。下面的程式也是之前的練習,部份來自Rhino的tutorial。主要的操作是建立一個Javascript執行環境,然後在global物件加入Count物件及print、printFile兩個函數。

 package tw.idv.fillano;
 
 import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.ScriptableObject;
 
 public class Counter extends ScriptableObject {
 	
 	private static final long serialVersionUID = -8999275232076586375L;
 
 	private int count;
 	public Counter() {
 	}
 
 	public Counter(Scriptable scope, Scriptable prototype) {
 		super(scope, prototype);
 	}
 
 	@Override
 	public String getClassName() {
 		return "Counter";
 	}
 	
 	public void jsConstructor(int a) {
 		count = a;
 	}
 	
 	public int jsGet_count() {
 		return ++count;
 	}
 	
 	public void jsFunction_resetCount() {
 		count = 0;
 	}
 }

(Counter.java)

然後是主程式:

 package tw.idv.fillano;
 
 import org.mozilla.javascript.*;
 import java.io.*;
 
 public class Main extends ScriptableObject {
 	private static final long serialVersionUID = 6156186852109434265L;
 
 	/**
 	 * @param args
 	 */
 	public static void main(String[] args) {
 		if(args.length<1) {
 			System.err.println("you must provide js file name as the first argument");
 			return;
 		}
 		Main main = new Main();
 		String[] names = {"print","printFile"};
 		main.defineFunctionProperties(names, Main.class, ScriptableObject.DONTENUM);
 		ContextFactory cf = ContextFactory.getGlobal();
 		Context cx = cf.enterContext();
 		try {
 			File fh = new File(args[0]);
 			FileReader fr = new FileReader(fh);
 			Scriptable scope = cx.initStandardObjects(main);
 			ScriptableObject.defineClass(scope, Counter.class);
 			cx.evaluateReader(scope, fr, fh.getName(), 1, null);
 		}
 		catch (Exception ex) {
 			ex.printStackTrace();
 		}
 		finally {
 			Context.exit();
 		}
 	}
 
 	public static void print(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
 		for (int i=0; i < args.length; i++) {
 			if (i > 0)
 				System.out.print(" ");
 			String s = Context.toString(args[i]);
 			System.out.print(s);
 		}
 		System.out.println();
 	}
 
 	public static void printFile(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
 		try {
 			if (args.length!=1) return;
 			
 			String s = Context.toString(args[0]);
 			BufferedReader br = new BufferedReader(new FileReader(s));
 			String line;
 			while((line=br.readLine()) != null) {
 				System.out.println(line);
 			}
 		}catch(Exception e) {
 			e.printStackTrace();
 		}
 	}
 	
 	@Override
 	public String getClassName() {
 		return "global";
 	}
 }

(Main.java)

從程式可以看出,先用ContextFactory產生GlobalContext,然後進入這個context,把Main設為這個context的scope,同時把Main的幾個static method使用defineFunctionProperties方法定義為global的函數,另外使用ScriptableObject.defineClass()給這個scope加入Counter物件。最後利用這個context的evaluateReader方法透過FileReader讀取Javascript檔案來執行。

接下來用一個Javascript檔案做測試:

 function add(x,y){return x+y;}
 print(add(3,4));
 var a=new Counter(7);
 print(a.count);
 printFile('test01.js');

(test01.js)

跑出的結果:

 7
 8
 function add(x,y){return x+y;}
 print(add(3,4));
 var a=new Counter(7);
 print(a.count);
 printFile('test01.js');


(要怎麼下載Rhino,怎麼設定編譯環境,怎麼執行...這些就不提了)


上一篇
Javascript面面觀:應用篇《SpiderMonkey》
下一篇
Javascript面面觀:應用篇《V8》
系列文
Javascript面面觀30

尚未有邦友留言

立即登入留言