일반 Java6 에서 지원하는 Scripting (번역중)

황제낙엽 2010.05.28 09:08 조회 수 : 216 추천:113

sitelink1 http://www.javainthebox.net/laboratory/J...pting.html 
sitelink2  
sitelink3  
sitelink4  
sitelink5  
sitelink6  

언어중의 언어 - Scripting

시작하며
2004년의 JavaOne에서, Java에서 스크립트를 Support 한다는 발표가 있었습니다.
특히 서버사이드에서의 사용을 전제로 하는 것 같은데, PHP가 가장 먼저 Support 되는 언어가 되었습니다.

물론 표준책정은 JCP에서 하고 있습니다. 구체적으로는 JSR-223 Scripting for Java Platform 에서 표준책정되고 있습니다.

그리고 그 기능이 Java SE 6 에서 받아들여지게 되었습니다.

하지만, PHP가 아니었습니다. Java SE 6에서 표준으로 Support 되는 것은 JavaScript 입니다.

JSR-223 은 Java 중에서 스크립트 언어를 사용하기 위한 표준으로, 스크립트 언어의 종류를 제한하고 있지는 않습니다.
Draft 에서 예로 사용되고 있는 것은 PHP, JavaScript, Kawa Scheme 입니다.

같은식의 프레임워크로써 Apache 의 Bean Script Framework 가 있습니다.

아무래도, PHP 는 서버 사이드로써, Java EE 에서 등돌린 것 같습니다. Java WSDP 의 옵션패키지로 Scripting for them Java Platform Cobundle 이라고 하는 것이 있습니다만
왠지 Java WSDP 의 페이지부터는 이 옵션의 링크가 삭제되어 있습니다.

우선은 JavaScript 부터 사용해 보지요.

Java SE 6 의 JavaScript Engine 은 Mozilla 의 Rhino 가 사용되고 있습니다.

한편, 여기에서 사용되고 있는 JDK 는 2005.10.6 에 공개된 build 55 입니다. 그래서, 이후 버전에 따라 동작하지 않을 수 있으니 양해해 주십시요.


JavaScript 를 Command 명령어로 사용해 보자
Java SE6 에는 JavaScript 를 사용해볼만한 좋은 Command 명령어인 jrunscript 가 표준으로 제공 되고 있습니다.
이것을 사용하여, JavaScript 를 실행해 봅니다.

처음에는 사용법을 조사하기 위해 Help 를 살펴봅시다.

 

C:temp>jrunscript -help  
Usage: jrunscript [options] [arguments...]  
 
where [options] include:  
  -classpath <path>    Specify where to find user class files  
  -cp <path>           Specify where to find user class files  
  -D<name>=<value>     Set a system property  
  -J<flag>             Pass <flag> directly to the runtime system  
  -l <language>        Use specified scripting language  
  -e <script>          Evaluate given script  
  -encoding <encoding> Specify character encoding used by script files  
  -f <script file>     Evaluate given script file  
  -f -                 Interactive mode, read script from standard input  
                       If this is used, this should be the last -f option  
  -help                Print this usage message and exit  
  -?                   Print this usage message and exit  
  -q                   List all scripting engines available and exit  
 
If [arguments..] are present and if no -e or -f option is used, then first  
argument is script file and the rest of the arguments, if any, are passed  
as script arguments. If [arguments..] and -e or -f option is used, then all  
[arguments..] are passed as script arguments. If [arguments..], -e, -f are  
missing, then interactive mode is used.  
C:temp> 


여기에는 써있지 않습니다만, -l 을 지정하지 않으면 default 로 JavaScript 가 지정됩니다. 또, 인터렉티브 모드는 -f- 를 사용하도록 기술되고 있습니다만,
-f 혹은 -e 를 사용하지않으면 인터렉티브 모드로 되는 것 같습니다.

-q 를 사용하면 사용가능한 언어의 목록이 출력되므로 입력해 보겠습니다.

·미리보기 | 소스복사·
C:temp>jrunscript -q  
Language ECMAScript 0 implemention "Mozilla Rhino" @IMPLEMENTATION.VERSION@  
C:temp> 


JavaScript 를 사용할 수 있는 것은 알고 있습니다만, 아직 버그가 있는것 같네요. ^^;; 게다가 -l 의 이후에 기술 가능한 문자열을 써주지 않는 것은 조금 문제입니다.

실제로 -l ECMAScript 라고 지정하면, 그런 언어를 사용할 수 없다고 합니다.


·미리보기 | 소스복사·
C:temp>jrunscript -l ECMAScript  
   
script engine for language ECMAScript can not be found  
   
C:temp> 


이것은 좀 수긍할 수 없네요. 제대로 -l js 라고 기술 가능한 정보를 표시하지 않으면...

겨우, JavaScript 를 사용할 수 있는 곳까지 도달했습니다. 제일 먼저는 역시 Hello, World! 로 합니다.
jrunscript 라고만 입력하면, 프롬프트가 출력됩니다.

C:temp>jrunscript

js>
이걸로 인터렉티브 모드가 시작됩니다. 여기에서, print 메소드를 사용하고, Hello, World! 를 출력해봅니다.

C:temp>jrunscript

js> print('Hello, World!');

Hello, World!

js>
간단하게 동작합니다.
-e 를 사용하는 경우에는 double quotation으로 둘러쌀 필요가 있는 것 같습니다.

C:temp>jrunscript -e "print('Hello, World!');"

Hello, World!

C:temp>
아무것도 둘러싸지 않거나, single quotation으로 둘러싸면 예외가 발생합니다.

C:temp>jrunscript -e print('Hello, World!');

script error: sun.org.mozilla.javascript.internal.EvaluatorException: unterminated string literal (#1) in  at line number 1

C:temp>

또, 스크립트 안에서 double quotation 을 사용하는 경우는, "" 역슬래쉬로 escape 하여 "" 역슬래쉬로 표기하지 않으면 안되는 것 같습니다.
인터렉티브 모드 혹은 -e 로 스크립트를 실행하는 경우, for 문이라던지 메소드등의 절은 1행에 쓰지않으면 안되는 것 같습니다.
완전한 인터렉티브 모드로써 이것은 방법이 없습니다.

실제로 인터렉티브 모드로 사용하지 않고 -f 로 스크립트 화일을 지정하는 것은 대부분이라고 생각하기 때문에 이것은 이거대로 좋다고 생각합니다.
-f 로 스크립트 화일을 지정하는 경우는 텍스트화일로 JavaScript 의 코드를 기술하면 됩니다.

 

jrunscript 를 해부한다
Java SE 6 와 지금까지의 Java SE 와의 가장 큰 차이점은 소스코드가 공개 초기 단계부터 제공되고 있다는 것입니다.
지금까지는 FCS 가 나온 후에 전체 소스코드가 공개되었습니다만, Java SE6 는 https://Java SE 6.dev.java.net/ 에서 매주 바이너리와 함께 소스코드가 제공되고 있습니다.

전체 소스코드가 제공되고 있는 것은, jrunscript 의 코드도 마찬가지입니다.

jrunscript 의 실체는 com.sun.tools.script.shell.Main 클래스입니다. 왜? 이런걸 알 수 있는가 하면. 소스가 인스톨 된 디렉토리에서 grep (Windows 라면 익스플로러의 검색) 으로 jrunscript 를 찾아 보았기 때문입니다.
그러자 j2se/make/sun/jrunscript/Makefile 이 발견되었습니다. 이것을 오픈하면, PACKAGE 에는 com.sun.tools.script.shell 로 기술되고 있습니다만, JAVA_ARGS 에는 com.sun.tools.script.shell.Main 으,로 기술되어 있습니다.

그러면, com.sun.tools.script.shell.Main 클래스의 소스를 봅시다. 여기에서의 사용법을 보면, Java 로부터 스크립트 언어를 어떻게 다룰지 이해할 수 있습니다.

우선 main메서드는 다음과 같습니다.

·미리보기 | 소스복사·
public static void main(String[] args) {  
    // parse command line options  
    String[] scriptArgs = processOptions(args);  
      
    // process each script command  
    for (Command cmd : scripts) {  
        cmd.run(scriptArgs);  
    }  
      
    System.exit(EXIT_SUCCESS);  

processOptions 메서드로 jrunscript 의 인수를 체크하고 그 후 for문 커맨드를 실행하는 느낌이네요.

이러한 processOptions 메서드입니다만, 이 메서드는 길기에 전부 볼 필요는 없습니다. 중요한 곳을 뽑아 내 봅시다. 우선, processOptions 메소드로부터 콜해지는 checkClassPath 메소드의 제일(가장) 마지막에 기술되고 있는 부분입니다.

·미리보기 | 소스복사·
engineManager = new ScriptEngineManager(); 
javax. script. ScriptEngineManager 클래스는 아무래도 스크립트 엔진의 팩토리클래스인 것 같습니다. ScriptEngineManager 오브젝트인 endingeManager 을 찾아 보면, listScriptEngines 메소드라고 하는 것이 있었습니다.

아무래도 jrunscript 의 -q 옵션을 지정했을 때에 콜해지는 메소드인 것 같습니다.

·미리보기 | 소스복사·
private static void listScriptEngines() {  
    List factories = engineManager.getEngineFactories();  
    for (ScriptEngineFactory factory: factories) {  
        getError().println(getMessage("engine.info",  
                new Object[] { factory.getLanguageName(),  
                        factory.getLanguageVersion(),  
                        factory.getEngineName(),  
                        factory.getEngineVersion()  
        }));  
    }  
    System.exit(EXIT_SUCCESS);  

이것을 보면 알 수 있듯이, ScritEngineManager#getEngineFactories 메소드로 스크립트 엔진을 모두 얻을 수 있는 것 같습니다.

지정된 스크립트 엔진을 생성하는 것은 getScriptEngine 메소드입니다.

·미리보기 | 소스복사·
private static ScriptEngine getScriptEngine(String lang) {  
    ScriptEngine se = engines.get(lang);  
    if (se == null) {  
        se = engineManager.getEngineByName(lang);  
        if (se == null) {  
            getError().println(getMessage("engine.not.found",  
                    new Object[] { lang }));  
                    System.exit(EXIT_ENGINE_NOT_FOUND);  
        }  
          
        // initialize the engine  
        initScriptEngine(se);  
        // to avoid re-initialization of engine, store it in a map  
        engines.put(lang, se);  
    }  
    return se;  

단지, ScriptEngineManager#getEngineByName 메소드로 얻을 수 있는 것 같습니다. 인수는 명확하다고 생각할정도로, 언어의 이름을 사용합니다. 그 이외에도 확장자, MIME 타입으로 지정하는 getEngineByExtension/getEngineByMimeType 이라고 하는 메소드가 있습니다.

스크립트 엔진을 얻게 되면, 스크립트를 실행합니다. 실행은 evaluateString 메소드등으로 수행됩니다.

·미리보기 | 소스복사·
private static Object evaluateString(ScriptEngine se,  
        String script, boolean exitOnError) {  
    try {  
        return se.eval(script);  
    } catch (ScriptException sexp) {  
        getError().println(getMessage("string.script.error",  
                new Object[] { sexp.getMessage() }));  
                if (exitOnError)  
                    System.exit(EXIT_SCRIPT_ERROR);  
    } catch (Exception exp) {  
        exp.printStackTrace(getError());  
        if (exitOnError)  
            System.exit(EXIT_SCRIPT_ERROR);  
    }  
      
    return null;  

예외처리는 실질적으로 1라인 뿐입니다. ScriptEngine#eval 메소드를 콜하고 있습니다. ScriptEngine 클래스의 Javadoc 를 보면, 6종류의 eval 메소드가 오버로드 되고 있습니다. 기본적으로는 인수가 문자열인가 Reader 오브젝트인가의 차이인 것 같습니다.

여기에서 일단 정리해 봅시다.

Java 안(속)에서 스크립트를 사용하기 위해서는

    1. ScriptEngineManager 오브젝트를 생성하는
    2. ScriptEngineManager#getEngineByName/getEngineByExtension/ getEngineByMime 메소드를 사용해서 ScriptEngine 오브젝트를 취득하는
    3. ScriptEngine#eval 로 스크립트를 실행한다

라고 하는 순서를 따르는 것이 좋다는 것을 알았습니다.

이런 연유로, 다음은 실제로 샘플을 만들어서 스크립트를 제어해 봅시다.

스크립트를 프로그램으로부터 실행한다
여기에서도 처음에는” Hello, World! ”로 합니다.

샘플의 클래스명은 ScriptSample1입니다.

            サンプルのソ?スコ?ド ScriptSample1.java
·미리보기 | 소스복사·
import javax.script.ScriptEngine;  
import javax.script.ScriptEngineManager;  
import javax.script.ScriptException;  
   
public class ScriptSample1 {  
    public ScriptSample1() {  
        ScriptEngineManager manager = new ScriptEngineManager();  
        ScriptEngine engine = manager.getEngineByName("js");  
          
        String script = "print('Hello, World!');";  
   
        if (engine != null) {  
            try {  
                engine.eval(script);  
            } catch (ScriptException ex) {  
                ex.printStackTrace();  
            }  
        }  
    }  
   
    public static void main(String[] args) {  
        new ScriptSample1();  
    }  

이전의 순서를 그대로 코딩한 것 뿐이므로, 설명은 필요없겠습니다.

이것을 실행해 보면, 다음과 같습니다.
C:temp>java ScriptSample1
Hello, World!
 
C:temp>
이상없이 실행되는 것으로 기분 좋게 이러한 코드를 써보았습니다.

                    サンプルのソ?スコ?ド ScriptSample2.java
·미리보기 | 소스복사·
ScriptEngineManager manager = new ScriptEngineManager();  
ScriptEngine engine = manager.getEngineByName("js");  
 
String[] scripts = {"function printHelloWorld() {",  
                    "    var text = 'Hello, World!';",  
                    "    for (var i = 0; i < 10; i++) {",  
                    "        print(text);",  
                    "    }",  
                    "}",  
                    "printHelloWorld();"};  
 
if (engine != null) {  
    try {  
        for (String script: scripts) {  
            engine.eval(script);  
        }  
    } catch (ScriptException ex) {  
        ex.printStackTrace();  
    }  

단지” Hello, World! ”을 10회 표시하는 코드입니다.

그러나 이것을 실행하면 ...

C:temp>java ScriptSample2
javax.script.ScriptException: sun.org.mozilla.javascript.internal.EvaluatorExcep
tion: missing } after function body (#1) in  at
line number 1
        at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.ja
va:93)
        at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.ja
va:108)
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:241)

        at ScriptSample2.(ScriptSample2.java:21)
        at ScriptSample2.main(ScriptSample2.java:30)
 
C:temp>

역시, 문장을 1줄로 작성하지 않으면 안되는 것 같습니다.

·미리보기 | 소스복사·
ScriptEngineManager manager = new ScriptEngineManager();  
ScriptEngine engine = manager.getEngineByName("js");  
 
String[] scripts = {"function printHelloWorld() {" 
                    + "    var text = 'Hello, World!';" 
                    + "    for (var i = 0; i < 10; i++) {" 
                    + "        print(text);" 
                    + "    }" 
                    + "}",  
                    "printHelloWorld();"};  
 
if (engine != null) {  
    try {  
        for (String script: scripts) {  
            engine.eval(script);  
        }  
    } catch (ScriptException ex) {  
        ex.printStackTrace();  
    }  

이것으로 괜찮아 졌습니다. 실행하면 잘 표시됩니다.

C:temp>java ScriptSample2
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!

C:temp>
다음은 파일로부터 스크립트를 읽어 봅니다.

                        サンプルのソ?スコ?ド ScriptSample3.java
·미리보기 | 소스복사·
public ScriptSample3(String filename)  
        throws FileNotFoundException, IOException {  
    FileReader reader = new FileReader(filename);  
 
    ScriptEngineManager manager = new ScriptEngineManager();  
    ScriptEngine engine = manager.getEngineByName("js");  
 
    if (engine != null) {  
        try {  
            engine.eval(reader);  
        } catch (ScriptException ex) {  
            ex.printStackTrace();  
        }  
    }  
 
    reader.close();  

샘플로 만든 JavaScript 파일 sample. js 은

·미리보기 | 소스복사·
function printHelloWorld() {  
    var text = 'Hello, World!';  
    for (var i = 0; i < 10; i++) {  
        print(text);  
    }  
}  
    
printHelloWorld(); 
실행한 결과는 조금전에와 같습니다.

C:temp>java ScriptSample3 sample.js
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
 
C:temp>

 

 

 

 

 

 


스크립트로부터 Java 에 액세스한다
지금까지는 단지 스크립트를 Java 로부터 실행하는 것 뿐이었습니다만, 그것만이라고 하기엔 좀 이상하지 않을까요? 역시나, 스크립트 안에서 Java 의 클래스나 오브젝트에 접근할 수 없다면...

이것에 관해서는 Javadoc 을 살펴봐도 별 방법이 없으므로, JSR-223 Scripting for Java Platform 의 드래프트를 읽어 보았습니다.

드래프트는 160페이지 이상도 있습니다. 읽기가 귀찮다 라고 생각되었지만 절반은 Javadoc 이었습니다 ^^;;

그래서, 스크립트 안에서 Java 에 액세스하는 방법입니다.

이것은 스크립트 언어의존에 대한 이야기입니다만, 드래프트에는 PHP 와 JavaScript 와 Kawa Scheme 의 예가 실려 있습니다.

아까의 샘플 ScriptSample3을 사용해서 다음 스크립트 date. js 을 실행해 봅니다.

·미리보기 | 소스복사·
importPackage(java.util);  
   
var date = new Date();  
print(date);  
print('Milliseconds: ' + date.getTime());  
   
importPackage(java.lang);  
   
print('Current Milliseconds: ' + System.currentTimeMillis()); 
대부분 보통 JavaScript 와 같이 사용할 수 있습니다. 단, 클래스를 사용하기 전에 importPackage 메소드를 콜 할 필요가 있는 것 같습니다.

오브젝트의 생성에는 new 을 사용하고, 메소드는 콤마(.)로 기술합니다.

클래스 메소드도 특별히 차이점이 없네요. 단, Java 의 프로그램과 달리 java.lang 패키지라고 할지라도, importPackage 가 필요한 것 같습니다.

그러면, 실행해 봅시다.

C:temp>java ScriptSample3 date.js
Fri Oct 14 2005 04:53:34 GMT+0900 (JST)
Milliseconds: 1129244014953
Current Milliseconds: 1129244015015
 
C:temp>
특별히 문제없이 실행할 수 있었습니다.

그러나, importPackage 메소드를 사용하면, java.awt.List 와 java.util.List 와 같이 충돌이 발생하는 이름을 사용할 수 없는 것 같은 생각이 듭니다.

드래프트에는 importPackage 메소드를 사용한 예로써 기술되고 있지 않았습니다만, 조금 시도해 봅시다.

먼저 말한 date.js 을 다음과 같이 변경해서 실행해 봅니다.

·미리보기 | 소스복사·
var date = new java.util.Date();  
print(date);  
print('Milliseconds: ' + date.getTime());  
    
print('Current Milliseconds: ' + java.lang.System.currentTimeMillis()); 
 

C:temp>java ScriptSample3 date2.js
Fri Oct 14 05:10:21 JST 2005
Milliseconds: 1129245021765
Current Milliseconds: 1129245021937
 
C:temp>
importPackage 를 하지 않고, 직접적인 패키지 클래스를 지정하는 것으로 실행할 수 있었습니다.

여기에서 주의하지 않으면 안될 것이 있습니다. JavaScript 에서 Java 의 메소드를 콜할 경우, 메소드의 인수가 자동적으로 Java 의 형식으로 변경됩니다. 그러나, JavaScript 는 동적인 언어이므로 Java 의 형과 일대일로 대응하지 않는다 라는 것입니다.

기본적으로는 문자열쪽이 수치보다 먼저 들어 맞는 것 같습니다.

즉 foo(String s)과 foo(int x)을 JavaScript 에서 x.foo(100)이라고 기술했을 때에는 x.foo(”100”)이 호출된다는 것입니다.

이렇게 세세한 부분은 직접 해 보지 않으면 모를 부분이 많네요.


GUI 도 조작해 본다

모처럼이므로, GUI 도 해 볼까요? 다음과 같은 스크립트 frame.js 을 실행해 보았습니다.

·미리보기 | 소스복사·
importPackage(javax.swing);  
   
var frame = new JFrame("Sample");  
frame.setVisible(true); 
 

C:temp>java ScriptSample3 frame.js
javax.script.ScriptException: sun.org.mozilla.javascript.internal.EcmaError: Ref
erenceError: "javax" is not defined. (#1) in  at
 line number 1
        at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.ja
va:93)
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:227)

        at ScriptSample3.<init>(ScriptSample3.java:17)
        at ScriptSample3.main(ScriptSample3.java:28)
 
C:temp>

에구, 안 되는 것 같습니다. javax라고 하는 패키지는 없다고 하네요. 이외에도 조사해 보았지만, javax나 org 에서 시작되는 패키지는 전부 사용못하는 것 같습니다.

Rhino 의 문서를 읽어 보면, importPackage 을 사용하지 않고, 대신 Packages 로 붙이면 괜찮은 것 같습니다.

·미리보기 | 소스복사·
importPackage(java.awt);  
   
var frame = new Packages.javax.swing.JFrame("Sample");  
frame.setLayout(new FlowLayout());  
   
var button = new Packages.javax.swing.JButton("OK");  
frame.add(button);  
   
frame.setSize(100, 100);  
frame.setVisible(true); 
대단한 것은 없습니다. 그저 프레임을 만들어서 거기에 버튼을 붙이고 있는 것 뿐입니다.

이것을 실행하면, 제대로 프레임이 표시되었습니다. 그러나, 버튼을 클릭해도 클로즈 버튼을 클릭해도 아무 것도 일어나지 않습니다.

모처럼이므로, 이벤트 처리를 계속해 봅시다.

JavaScript 이므로, 동적으로 메소드를 정의 할 수 있습니다만, Java 의 오브젝트에 대하여도 그것을 실시할 수 있을지 모르겠습니다. 아마도, 무리라고는 생각합니다만....

·미리보기 | 소스복사·
importPackage(java.awt);  
   
var frame = new Frame("Sample");  
frame.setLayout(new FlowLayout());  
   
var button = new Button("OK");  
button.processMouseEvent = function(event) {  
    java.lang.System.exit(0);  
}  
   
frame.add(button);  
   
frame.setSize(100, 100);  
frame.setVisible(true); 
이것을 실행해 보면

C:temp>java ScriptSample3 frame3.js
javax.script.ScriptException: sun.org.mozilla.javascript.internal.EvaluatorExcep
tion: Java class "java.awt.Button" has no public instance field or method named
"processMouseEvent". (#7) in  at line number 7
        at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.ja
va:93)
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:227)

        at ScriptSample3.<init>(ScriptSample3.java:17)
        at ScriptSample3.main(ScriptSample3.java:28)
    
C:temp>

순간 안되는 것인가?라고 생각했습니다만, 에러·메세지를 잘 읽어 보면 public 한 메소드가 없다고 써있는게 아닙니까? 시험 삼아 public 한 메소드라도 해 본 것입니다만, 정말로 이 방법은 안 되는 것 같습니다.

Rhino 의 문서를 보면, 아무래도 인터페이스를 implement한 익명클래스는 스크립트내에서 만들 수 있는 것 같습니다.

확실히 processMouseEvent 메소드는 protected 이므로, public 한 메소드로 시도해 봅시다. 제일 간단한 예로 toString 메소드로 시도해 봅니다.

·미리보기 | 소스복사·
importPackage(java.awt);  
 
var frame = new Packages.javax.swing.JFrame("Sample");  
frame.setLayout(new FlowLayout());  
   
var button = new Packages.javax.swing.JButton("OK");  
   
obj = {actionPerformed: function(event) {  
    print("ActionPerfomed: " + event);  
}}  
   
listener = new java.awt.event.ActionListener(obj);  
button.addActionListener(listener);  
   
frame.add(button);  
   
frame.setSize(100, 100);  
frame.setVisible(true); 
쓰는 방법은 조금 번거롭습니다만 가능한게 어디입니까? 실행하고, 버튼을 클릭해 보면,

C:temp>java ScriptSample3 frame4.js
ActionPerfomed: java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=OK,when=11296798
29624,modifiers=Button1] on javax.swing.JButton[,32,5,51x26,alignmentX=0.0,align
mentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@1a8c
4e7,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon
=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,b
ottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=
true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=OK,defaultCapable=tr
ue]
 
C:temp>
오오, 실행되었습니다.


이미 존재하는 오브젝트에 접근한다
이전까지는, 스크립트 안에서 오브젝트를 생성하고, 그 오브젝트에 대하여 메소드 콜을 하고 있었습니다.

그러나, 스크립트를 실행할 때에는, 이미 Java 측에서 존재하고 있는 오브젝트가 여러가지 있을 것입니다. 이것들의 오브젝트를 스크립트로부터 액세스할 수는 없을까요?

바로, 해 봅시다.

오브젝트에 액세스하기 위해서는, 스크립트 안의 변수와 오브젝트의 대응표를 만들지 않으면 안되는 것 같습니다. 이것 때문에 사용되는 것이 Bindings 인터페이스입니다.

드래프트에서는 Bindings 인터페이스는 Namespace 인터페이스가 되고 있습니다만, 변경된 것 같습니다.

Bindings 인터페이스는 Map <String, Object>의 파생 인터페이스가 되고 있으므로, 사용방법은 Map 과 거의 같습니다.

                   
                    サンプルのソ?スコ?ド ScriptSample4.java
·미리보기 | 소스복사·
public ScriptSample4() {  
    String script = "sample.foo('bar');";  
    StringReader reader = new StringReader(script);  
 
    ScriptEngineManager manager = new ScriptEngineManager();  
    ScriptEngine engine = manager.getEngineByName("js");  
 
    if (engine != null) {  
        Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);  
        bindings.put("sample", this);  
          
        try {  
            engine.eval(reader);  
        } catch (ScriptException ex) {  
            ex.printStackTrace();  
        }  
    }  
 
    reader.close();  
}  
 
public void foo(String msg) {  
    System.out.println("Foo: " + msg);  

Bindings 오브젝트를 새롭게 생성할 수도 있습니다만 (ScriptEngine#createBindings 을 사용), 여기에서는 이미 ScriptEngine 오브젝트가 보유하고 있는 Bindings 오브젝트에 추가합니다.

ScriptEngine#getBindings 메소드의 인수는 Bindings 의 스코프를 나타내고 있습니다. ScriptContext. ENGINE_SCOPE 이 엔진마다 보유하는 스코프로, ScriptContext. GLOBAL_SCOPE 이 ScriptEngineManager 이 보유하는 스코프가 됩니다.

이 예에서는 ENGINE_SCOPE 로 해서, sample 에 대응하는 것이 SimpleScript4 오브젝트를 대응시키고 있습니다.

실제 스크립트는 script 변수로 나타나고 있습니다만, sample 에 대하여 foo 메소드를 콜하는 것 뿐입니다.

잘 되면 SimpleScript4#foo 이 콜 될 것입니다. 해 봅시다.

C:temp>java ScriptSample4
Foo: bar
 
C:temp>
이 기능을 사용하면, 임의의 오브젝트를 스크립트로부터 액세스하는 것이 가능 할 것 같습니다.

컴파일과 인보크
자주 사용하는 것이지만 변경이 거의 없는 스크립트가 있습니다.

그러한 스크립트는 사전에 컴파일해 두고, 고속으로 호출하는 것이 가능합니다.

단, 이 컴파일의 기능은 스크립트 엔진에 의존하고 있으므로, 할 수 없는 것도 있습니다. 다행히, Rhino 는 할 수 있는 것 같습니다.

컴파일이 가능한 스크립트 엔진은 Compilable 인터페이스를 implement 하고 있습니다. 우선, Rhino 에서 implement 하고 있는 것인가 아닌가 확인해 봅시다.

                サンプルのソ?スコ?ド ScriptSample5.java
·미리보기 | 소스복사·
public ScriptSample5() {  
    ScriptEngineManager manager = new ScriptEngineManager();  
    ScriptEngine engine = manager.getEngineByName("js");  
 
    if (engine != null) {  
        Class cls = engine.getClass();  
        System.out.println("Engine Class: " + cls);  
        System.out.println("Interfaces:");  
        for (Class ifClass : cls.getInterfaces()) {  
            System.out.println(ifClass);  
        }  
    }  

실행해 보면 다음과 같습니다.

C:temp>java ScriptSample5
Engine Class: class com.sun.script.javascript.RhinoScriptEngine
Interfaces:
interface javax.script.Invocable
interface javax.script.Compilable
 
C:temp>

번호 제목 글쓴이 날짜 조회 수
191 Servlet의 각종 Listener 사용방법 및 샘플 황제낙엽 2010.10.26 83
190 ServletContext 초기화 및 소멸 황제낙엽 2010.10.26 75
189 java.lang.Object 객체 소멸 - finalize() 황제낙엽 2010.10.08 35
188 Array 또는 List 의 Sort (목록 소트) 황제낙엽 2010.09.14 27
187 Class.getResource() 와 ClassLoader.getResource()의 차이점 황제낙엽 2010.06.25 20
186 Designing RMI Applications 황제낙엽 2010.06.24 505
185 Java Node to String Conversion 황제낙엽 2010.06.10 54
184 Java SE 6 Mustang 5장 스크립팅 기능 (번역중) 황제낙엽 2010.06.10 21
» Java6 에서 지원하는 Scripting (번역중) 황제낙엽 2010.05.28 216
182 RMI 시작하기(2) file 황제낙엽 2010.05.27 12
181 RMI 시작하기(1) file 황제낙엽 2010.05.27 67
180 Java Remote Method Invocation (Java RMI) 황제낙엽 2010.05.27 51
179 javax.script API 관련 스크랩 (ScriptEngine, ScriptEngineManager) 황제낙엽 2010.05.25 112
178 java.util.Properties 파일 사용 예제 file 황제낙엽 2010.04.06 68
177 10진수 <-> 16진수(Hex) 변환 file 황제낙엽 2010.03.29 1225
176 ServletConfig 이용하기 황제낙엽 2010.03.15 22
175 16비트 CRC 체크용 클래스 (사용자 클래스) 황제낙엽 2010.03.14 406
174 파일을 읽어서 CRC 값을 연산하는 메서드 (java.util.zip.CRC32) 황제낙엽 2010.03.14 137
173 byte배열에 대한 CRC 를 계산하는 메서드 (java.util.zip.CRC32) 황제낙엽 2010.03.14 2166
172 java의 List와 반복문(loop), 그리고 변수 선언 위치에 대해서 황제낙엽 2010.02.17 182