Java에서 AppleScript 실행하기

Open External 플러그인을 만들면서 겪었던 문제 상황을 정리한다. 이클립스에서 터미널(Terminal)을 오가는 작업이 귀찮아 원하는 위치에 터미널을 띄우려고 찾다보니 애플스크립트(AppleScript)까지 넘어왔다. 자바에서 애플스크립트를 실행하는 방법은 크게 NSFoundation 클래스를 사용하는 방법과 /usr/bin/osascript 를 이용하여 커맨드 라인에서 직접 스크립트를 실행하는 방식이 있다. 개인적으로는 외부 프로세스를 이용하는 방식을 선택했다. 자바와 애플스크립트의 두 가지 상호운용 방식에 대해서는 설명하고 있는 기사를 참고하자.

 

여기서 osascript에 파일로 스크립트를 전달하면 문제가 없으나, 문자열로 스크립트를 넘기면 따옴표(')를 인식하지 못하고 Unknown Token 에러를 뿜어낸다는 문제가 있다. 물론 실제 커맨드라인에서는 정상적으로 동작한다. 찾아보니 이 문제로 고생한 사람이 몇 보이지만 이렇다할 해결책은 아직 찾지 못했다. 임시로 사용하는 명령어를 위해서 파일을 작성해야 한다는 점이 못마땅하지만 Unknown Token에러를 피하기 위해서는 이 방식을 이용하자.

 

아래 예제는 Open External 플러그인에서 Mac OS의 터미널 애플리케이션을 실행하는데 사용하는 코드의 일부분이다. 자바와 애플스크립트의 상호작용 예제로 참고만 하자. 임시 경로에 파일을 작성하고 해당 파일을 osascript에 전달했다. 저리 긴 스크립트를 문자열로 넘겨서 생긴 문제가 아니라, 터미널 실행 테스트 단계에서 한 줄짜리 스크립트를 넘겨도 문제가 생겼다.  터미널 애플리케이션을 띄우는 정말 사소한 플러그인이지만 나름의 문제를 겪게 되더라는 사실!

  1. // run
  2. File script = writeScript(path, openInNewTab());
  3. Process process = new ProcessBuilder("/usr/bin/osascript", script.getAbsolutePath()).start();
  4. int exitCode = process.waitFor();

 

  1. // write apple script to open mac os x terminal app.

    private File writeScript(String terminalPath, boolean useTab) throws IOException {
            File file = new File(System.getProperty("java.io.tmpdir"), "eclipse.applescript");
            PrintWriter writer = null;
           
            try {
                writer = new PrintWriter(new FileWriter(file));
               
                String script = "    do script \"cd '" + terminalPath + "';/usr/bin/clear\"";
               
                writer.println("tell application \"System Events\"");
                writer.println("    set windowCount to count(processes whose name is \"Terminal\")");
                writer.println("end tell");
               
                writer.println("tell application \"Terminal\"");
                writer.println("    activate");
       
                if (useTab) {
                    writer.println("    if windowCount is greater than 0 then");
                    writer.println("        tell application \"System Events\" to tell process \"Terminal\" to keystroke \"t\" using command down");
                    writer.println("    end if");
                    writer.println(script + " in window 1");
                } else {
                    writer.println("    if windowCount is equal to 0 then");
                    writer.println(script + " in window 1");
                    writer.println("    else");
                    writer.println(script);
                    writer.println("    end if");
                }
                writer.println("end tell");
            } finally {

                if (writer != null)
                   writer.close();
            }
           
            return file;
        }