Whiteship's Note


OSGi 패키지가 아니라 서비스야 말로 진정한 Dynamic

Spring DM/exercise : 2008.08.20 22:05


번들과 번들 사이에서 자신이 가지고 있는 정보를 공유하는 방법은 두 가지 입니다. 패키지 안에 있는 클래스들을 공개해서 상대방이 내가 가진 클래스의 객체를 만들어서 사용하게 할 것이냐, 아니면 내가 패키지 말고 내가 객체를 만들어서 제공할 것이냐. 후자가 바로 서비스. 전자는 패키지입니다.

차이는 매우 큽니다. Dynamic Module System에서 Dynamic 이라고 할려면 사실 상 패키지 공개로는 아무 의미가 없고,  Service-Export/Import를 해야 의미가 있습니다. 왜냐면 말이죠... 생각을 해보면 됩니다.

A 번들 whiteship 패키지에 Whiteship.java 클래스가 있고,
B 번들 blueship 패키지에 Blueship.java 클래스가 있다고 하겠습니다.

이 때 B 번들에서 A 번들에 있는 Whiteship 타입의 객체가 필요합니다. 그래서 A 번들에서 Export-Package로 whiteship 패키지를 등록하고, B 번들에서는 Import-Package로 whieship을 등록했습니다. 그런 다음 B 번들에서 Whiteship 타입 객체를 만들어서 사용합니다.

자 A 번들의 Whiteship.java 클래스가 바꼈습니다. 어떡할건가요? A 번들을 다시 설치합니다. B 번들에서 사용하고 있는 Whiteship 타입 객체는요?? 그대로죠. 뭐 변한게 없습니다.  뭐가 동적으로 바뀌죠? 바뀌는거 없죠? 이제 뭔가요? 이제 Dynamic 인가요? 아니죠.

패키지 대신 서비스로 단어를 바꿔서 다시 생각해보시면 뭔가가 달라집니다.

다시, (서비스를 사용한다는 가정하에) A 번들의 Whiteship.java 클래스가 바꼈습니다. A 번들을 다시 설치해야겠군요. 이건 당연한 겁니다. 설마 A 번들도 다시 설치하는데 이게 뭐가 동적이야??? 라고 생각하시는 분은 안 계시죠? 만약에 그러면 그건 좀 코메디입니다. ㅋㅋ 아무튼 잡담이었구요. 자. 이 다음엔 어떤 일이 벌어질까요? B 번들이 사용하면 Whiteship.java 타입의 서비스까지 바뀝니다. 캬~~ 놀랍죠? 어떻게 바뀌냐구요? A 번들이 죽을 때 자기가 등록한 서비스들도 전부 죽입니다. 그럼 B 번들이 사용하고 있던 Whiteship 타입의 서비스도 죽었겠죠. 그런 다음 A 번들이 다시 살아나면, 서비스를 등록하고 그럼 그 서비스를 기다리고 있던 B 번들이 다시 Whiteship 타입의 서비스를 사용하는 겁니다.

복잡하죠? 네.. 사실 이렇게 해피한 시나리오대로 흘러가지 않을 가능성도 많습니다. 설정하기 나름입니다. 대기 시간 설정이라덩가. 서비스의 필수 여부 설정이라덩가. 덩덩덩.

그리고 또 있습니다.

패키지로 export/import  할 때는 impl 까지도 export 해줘야겠죠? 객체를 생성하려면 어차피 구현한 클래스까지 알아야 할테니까요. 그런데 서비스로 공개할 떄는 구현체까진 안 알려줘도 됩니다. 구현은 감추고 인터페이스만 공개할 수 있는거죠. 캬~

Anyway!! 패키지를 사용하는거 보다는 서비스를 사용하는게 진정한 Dynamic 이라는거 아시겠죠?

그럼? 서비스만 쓰지 패키지는 왜 있는거야?? 라는 생각.. 드시죠??

저도 좀 생각을 해봤는데요. GenericDao 같은 클래스의 서비스가 필요한가요? 그냥 상속해서 쓰면 그만이죠? 즉 이렇게 패키지로 공개할 것인가 서비스로 공개할 것이냐는 용도에 따라 좀 달라질 것 같습니다. 클래스가 필요하면 패키지로, 객체가 필요하면 서비스로. 글쵸? 그런거 같죠? ㅎㅎ; 저도 잘 몰라요.

top


Late Binding in Java

Spring DM/exercise : 2008.07.20 17:04


참조 : http://neilbartlett.name/blog/osgibook/

OOP의 목적 중 하나는 의존성을 최대한 낮춰서 코드의 재사용성과 유연함을 늘리는 것이다.

자바에서는 인터페이스를 사용해서 이런 목적에 접근 했다. 인터페이스는 불편하고 그것을 구현한 구현체만 바꾸면 인터페이스를 사용하고 있는 클라이언트 코드는 변경할 필요가 없었기 때문이다. 하지만, 한계가 있는데, 객체를 생성하려면 어차피 구현한 클래스를 클라이언트 쪽에서 알고 있어야 한다는 것이다.

그래서, 스캐너의 생성자를 사용해서 문자열을 받고 그 문자열로 분기문을 돌려서 구현체를 기반으로 객체를 만드는 코드를 사용하기도 하는데, 역시나 새로운 구현체가 생기거나 하면, 또 코드를 바꿔야 된다.

이에 대한 대안으로 스캐너를 사용하지 않고, "다른 뭔가"가 그 객체를 제공해주게 하는 것이다. 그래서 등장한게 "Assembler" 다. "Assembler"를 여러 곳에서 만들어 쓰다가 패턴이 발견되었고, 그 패턴을 구현한 프레임워크가 스프링과 구글쥬스다.

이 들을 Dependency Injection 프레임워크라고 한다. (물론 스프링은 그게 전부는 아니지만) 불행히도, 이런 Assembler Pattern도 정적이라는 특성 때문에 몇 가지 문제가 있다. 객체들의 연관 관계를 정적으로 한 번 생성하고 말기 떄문에, 객체 생성 순서를 신경 써야 한다. B가 A 객체를 참조하려면 A 객체를 먼저 만들어야 된다. 그리고 객체간에 상호 참조(Circular Dependency)도 조심해야 된다.

또 다른 문젠 동적으로 업데이트 하는 것이 불가능하다. 이렇게 정적으로 묶여있는 의존성 그래프에는, 아주 작은 객체 연관 관계를 변경하려고 해도 전체 시스템을 껐다가 켜야 한다.

OSGi는 바로 이 문제를 동적인 "서비스"를 이용해서 해결한다.

서비스는 DI 프레임워크에서의 빈 처럼 평번한 자바 객체(POJO)다. 서비스는 하나 이상의 인터페이스 이름으로 OSGi 서비스 레지스트리에 의해 제공된다. 서비스는 다른 서비스를 사용할 수 있고 고정적인 그래프로 묶이는게 아니라, 서비스는 언제든지 동적으로 등록되고 해지될수 있다. 따라서 서비스들 사이의 관계는 임시적인 관계이다.

객체 생성 순서 문제는 다음과 같이 해결한다. B가 A를 필요로 하는 상황에서 B 객체를 만들 때 A 객체가 있는지 화인하고 안 만들어져있으면 이 객체를 이용할 수 있을 때 까지 잠시 대기한다. 그리고 A 객체를 B가 이용하고 있는 도중에 A가 사라지고 새로운 A' 객체를 등록하면 서비스 B한테 이벤트를 날려서 교체하게 해준다.


인터페이스 -> Scanner -> Assembler -> DI Framework -> OSGi


top