2014-06-19

Rhino - JavaScript framework

 Rhino는 mozilla에서 개발한 Java로 구현된 JavaScript engine이다.

 과거 Netscape에서 Java로 구현된 navigator를 구현하려는 시도를 한 적이 있는데, 이때 사용했던 JavaScript engine이 Rhino engine의 전신이 된다.
 Javagator라고 불리던 이 프로젝트는 JavaScript를 Java byte code로 컴파일하여 실행하기 때문에 당시에 있던 다른 브라우저보다 빠른 성능을 낼 수 있을것을 기대했지만, JVM 자체의 성능 이슈와 다른 여러가지 상황때문에 중간에 중단되었지만, 일부 회사들의 지원으로 JavaScript framework은 분리되어 Rhino가 되었다.

 Rhino의 가장 큰 특징은 내부적으로 Reflection을 이용하여 JavaScript 코드에서 Java class를 그대로 가져다 쓸 수 있다는 것이다.
 또한 Java구현체를 그대로 사용할 수 있기 때문에, JavaScript engine 중에서는 특이하게 multi thread support가 된다는 특징을 가진다.

 JVM이 꾸준히 성장하여 많은 성능 개선을 이루었지만, WebKit이 사용하는 JSC(JavaScript Core)나 Google이 개발한 v8 engine도 내부적으로 JavaScript를 compile하기 때문에 Rhino가 가지는 성능상의 이점은 없다. 사실상 v8이나 jsc보다 느리다.

 성능상에 이점은 없지만, 반드시 Java를 사용해야 하거나 multi-core support가 필요한 일부 환경에서는 Rhino engine을 사용하는 경우가 있다. 하지만 이 중에 이름만 들어서 알만한 유명한 프로젝트는 없다.
 Rhino를 사용하는 가장 유명한 구현체는 RingoJS로 보인다.

2014-06-12

[java] shutdown hook 사용하기

 프로그램을 작성하다 보면, 프로세스가 종료될 때 반드시 실행해야 하는 코드가 나온다.
 exit 포인트가 하나뿐인 프로그램이라면, exit 하기 전에 실행하면 되지만, 보통 코드를 그렇게 작성하지 않기 때문에 Java에서는 Runtime의 shutdown hook을 이용한다.

 사용하는 방법은 간단하다. Runtime의 addShutdownHook을 이용해 필요한 hook을 추가하면 된다.
 process가 종료되기 시작하면 프로세스 내의 non-daemon thread들이 종료되기 시작한다. 모든 non-daemon thread들이 종료되면, 등록된 hook들이 실행된다.

 shutdown hook은 C나 C++의 atexit과 비슷한 역할을 하지만, 함수를 등록하는 것이 아닌 Thread를 등록한다는 것이 다르다.
 또한, atexit은 등록된 함수가 stack에 쌓여서 LIFO로 동작한다. 하지만 Java의 addShutdownHook은 등록된 Thread가 순서 없이 실행되기 시작하여 병렬적으로 돌아간다.
 hook들이 병렬적으로 돌아가기 때문에 hook의 순서를 보장하고 싶다면 같은 thread에서 실행되도록 hook을 작성해야 한다.
 물론, 일반적인 병렬 프로그래밍처럼 lock을 이용하여 순서를 보장할 수도 있다. 하지만, shutdown hook은 프로세스가 종료될 때 반드시 실행되고, hook이 종료될 때까지 정상적으로는 프로세스가 종료되지 않기 때문에, shutdown hook에서 dead lock이 발생하면 프로세스가 종료되지 않는다. 이러한 이유로 shutdown hook에서는 가능하면 lock을 사용하지 않고 로직을 작성하는 것을 권장한다.

 removeShutdownHook이라는 API도 존재한다. 이 API를 이용하여 더는 필요 없어진 hook을 제거할 수 있다. atexit을 사용할 때는 필요 없어진 hook을 제거하기 위해서는 전역 flag를 두어 flag를 설정하거나, custom한 stack을 구현해야 하는데 이럴 필요가 없어진 것이다.

 shutdown hook은 Thread이기 때문에 hook을 달아놓으면 종료될 때까지 idle한 Thread가 하나 생기는 것이므로 이것이 overhead가 되지 않을까 걱정하는 사람도 있다. 기본적으로 이것이 틀린 생각은 아니다. 하지만 대부분의 구현체에서 Thread는 실제로 시작하기 전까지는 thread stack을 할당하지 않기 때문에 걱정할 필요 없다. 물론 아주 약간의 overhead도 감당할 수 없는 embedded 환경에서는 이런 부분도 고려해야 하지만, desktop application이나 server 같은 환경에서 고려해야 할 문제는 아니다.

 shutdown hook을 사용할 때 조심해야 할 것이 2가지 있다.

 우선 shutdown hook은 최대한 짧게 작성되어야 한다.
 만약 프로세스의 종료가 머신의 종료에 의한 것이라면 JVM은 프로세스가 완전히 종료되는 것을 기다리지 않고, 일정 시간이 지난 후 종료한다. 이 때문에 shutdown hook이 실행되기 전에 종료될 수 도 있고, shutdown hook이 실행되는 도중에 종료될 수도 있다.

 두 번째로 shutdown hook을 사용하는데 조심해야 하는 것은 shutdown hook은 반드시 실행되는 것이 아니라는 것이다.
 shutdown hook은 정상적인 종료에서만 호출된다는 것이다.
 따라서 비정상적인 종료에서도 반드시 수행돼야 하는 일은 하나의 Java application만으로는 처리할 수 없다. 이런 것은 보통 Java application을 실행하는 스크립트를 작성하거나, Java application을 실행하는 application을 작성하여 처리한다.

 여기서 비정상인 종료라는 것은 에러코드를 반환하는 것이 아니다.
 System.exit으로 호출되는 경우 status 코드를 무엇으로 반환하든지 정상 종료이다.
 SIGTERM을 받아서 종료되는 경우에도 정상종료이다.
 SIGTERM을 받은 프로세스는 정상적으로 shutdown hook을 호출하고 Thread를 정리하고 죽는다.
 handle되지 않은 Exception이 발생하여 process가 종료되는 경우도 Java는 정상종료로 취급한다.

 위의 3가지 경우에는 문제없이 shutdown hook이 호출된다.

 그렇다면 비정상종료는 무엇일까?
 우선 대표적인 비정상 종료는 halt다.
 halt는 프로세스를 강제종료하는 것으로 실행되는 즉시 종료되므로 shutdown hook이 불리지 않는다.
 프로세스가 SIGKILL을 받는 경우도 비정상 종료이다. SIGTERM과는 달리 SIGKILL을 받으면 리소스를 해제하거나 shutdown hook이 불리지 않고 바로 종료된다.
 마지막으로 JVM에 문제가 발생해서 죽는 일도 있다.
 JVM에 문제가 발생하는 것은 매우 드문 일이다. 하지만 JVM 코드에 버그가 있어서 죽거나, JNI를 사용하다가 문제가 생기는 경우, shutdown hook이 호출되지 않는다.

2014-06-10

[Design Pattern] Loan pattern - resource를 안전하게 사용하기

 언젠가 썼던 글에서도 설명했듯이 C++에서는 RAII를 이용하여 Resource의 안전한 해제를 보장하는 것을 넘어 control flow를 제어하는 역할까지 해준다.
 하지만 Garbage Collection을 사용하는 C#이나 Java 같은 언어에서는 언제 메모리가 해제될지 모르기 때문에 RAII pattern을 사용할 수 없다. 그래서 코드의 실행을 보장하기 위하여 finally 구문이 생기게 된 것이다.

 try finally를 사용하는 일반적인 방법은 아래와 같다.

 exception이 발생할 수 있으면 try 구문으로 감싸고 반드시 실행시켜야 하는 코드를 finally에 두는 것이다.

 하지만 위의 코드는 딱 보기에도 재사용성이 떨어진다.
 다른 동작을 하기 위해서는 언제나 try / catch를 써야 해서 boilerplate한 코드가 반복되기도 한다.
 이를 해결하는 방법은 없을까?

 Scala에서는 이를 해결하기 위하여 resource를 빌려주는 방식을 자주 이용한다.
 resource의 management를 하는 함수(lender)가 있고, resource를 사용하는 함수(lendee)에게 빌려주어 잠시 사용하게 해주는 것이다.
 이를 이용하여 API의 encapsulation과 reusability를 올릴 수 있다.
 우선은 다음 예제를 보자.

 위의 예시에서는 executeSql이라는 함수가 connection string과 Statement를 인자로 받는 Function을 인자로 받는다(말은 복잡한데 실제로 복잡한건 아닌데....... 말로 설명하려니 복잡해졌다.).
 첫 번째 인자로 받은 connection string을 이용하여 Statement라는 resource를 만들어 관리하게 된다. 즉, executeSql이 lender가 되는 것이다.
 그리고 두 번째 인자인 Statement를 인자로 받는 Function을 landee로 삼아 자신이 만든 Statement를 빌려주어 원하는 작업을 수행하게 한다.

 이런 pattern을 resource를 관리하는 lender와 빌려서 사용하는 lendee로 나뉘기 때문에 lender-lendee pattern으로 부르는 사람도 있지만, 보통은 loan pattern이라고 부른다.

 loan pattern은 resource의 관리/사용을 구분하였기 때문에 resource를 안전하게 사용하는 것을 보장해준다.
 또한, 변하는 부분인 사용에 해당하는 부분만을 재정의하면 되기 때문에 손쉽게 코드를 재사용 가능하게 만들어주고, 함수를 library로 제공하게 되면 user들에게 resource의 할당과 해제를 숨길 수 있어서 encapsulation이라는 측면에서도 매우 좋다.
 실제로 scala library의 많은 부분은 loan pattern을 사용하고 있다.