관리 메뉴

I LOVE EJ

JNDI 본문

Os & Server/Jrun

JNDI

BeOne 2007. 10. 15. 17:00

JNDI의 소개

많은 J2EE개발자들이 환경 변수(environment entries), DataSource 객체, JMS 메시지 수신지(JMS message destinations) 그리고 엔터프라이즈 빈 홈 인터페이스(enterprise bean home interfaces)를 찾고자 Java Naming and Directory Interface (JNDI)를 이용한다. 하지만 많은 사람들은 JNDI에 대한 진정한 이해 없이 이러한 기능을 하는 코드를 단순히 복사해서 붙이고 고칠 뿐이다. 이 팁은 사용자의 엔터프라이즈 시스템에 배포된 리소스를 액세스하기 위해 JNDI를 사용하는 방법을 소개한다.

엔터프라이즈 애플리케이션들은 그것들의 특성상, 비즈니스 오퍼레이션을 지원하기 위해 여러곳에 배포된 리소스들을 한데 모아야 한다. 새로운 시스템이 생성되었다거나, 기존의 시스템이 업그레이드 되었다거나, 오래된 시스템이 더 이상 작동하지 않을 때 서비스들이 오가게 된다. 애플리케이션 서비스를 서로 분리하는 것은 시스템을 쉽게 유지/확장할 수 있게 한다. 하지만 서비스가 분리되었을 때, 각자의 역할을 제대로 수행하기 위해서는 서로를 찾아낼 수 있어야만 한다. 이 때가 바로 명명 서비스(naming services)와 디렉토리가 유용한 시점이다.

명명 서비스는 이름을 이용해서 객체나 객체에 대한 레퍼런스를 검색하는 방법을 제공한다. 그러한 객체로의 예는 메시지 큐(message queues), 데이터베이스 커넥션 팩토리(database connection factories), 환경 파라미터(environment parameters), 그리고 엔터프라이즈 빈과 같은 분산 컴포넌트(distributed components)등이 있다. 애플리케이션 개발자들은 명명 서비스내의 이름에 객체들을 바인딩해서 객체에 이름을 붙인다. 애플리케이션 코드는 이렇게 바인딩된 이름으로 객체들을 검색하기 위해 명명 서비스를 사용할 수가 있다. 이러한 분리(decoupling)는 네트워크 객체들을 사용하는 시스템 컴포넌트에 대한 어떠한 변경없이 유지 보수를 위해 올리거나 내릴 수 있고, 요청들을 리다이렉트(redirect)시킬 수 있으며, 서비스가 다이나믹하게 재조정될 수 있음을 의미한다.

이미 기존의 명명 서비스에 대해 잘 이해하고 있으리라고 생각된다.

  • DNS (Domain Name Service)는 java.sun.com과 같은 호스트네임을 %nslookup java.sun.com%과 같은 IP주로소 매핑한다.
  • CORBA (Common Object Request Broker Architecture)를 위해 쓰이는 COS (Common Object Services) 명명 서비스 는 CORBA 인터페이스 이름(interface names)을 객체 인터페이스로 매핑한다.

사용자는 컴퓨터의 파일시스템을 파일의 경로(pathname)에 파일의 컨텐츠를 매핑하는 일종의 명명 서비스로 생각할 수 있다.

밑의 그림은 명명 서비스가 서비스 네임을 데이터나 서비스 인터페이스로 매핑하는 방법을 보여주고 있다.

figure 1

이름(name)을 객체로 매핑하는 것을 바인딩이라 부른다. 바인딩은 명명 서비스를 형성하는 사람에 의해 생성된다. 대부분의 명명 서비스는 프로그램이 런타임시에 이름을 객체로 바인딩하거나 해제하는 방법도 제공한다.

컨텍스트는 이름을 객체로 바인딩한 집합이다. 예를 들면, 파일시스템에서 경로 /home 는 흔히 시스템의 유저 디렉토리를 포함하는 컨텍스트이다. 컨텍스트는 다른 컨텍스트를 포함할 수도 있다. /home 컨텍스트의 유저 디렉토리는 그 자체가 유저 파일을 포함하는 컨텍스트이다.

컨텍스트는 최소한 명명 규칙(naming convention)과 검색기능(lookup function)을 갖는다. 예를 들면, DNS는 가장 구체적인 스트링은 왼쪽, 도메인은 오른쪽에 나타내면서 스트링을 점으로 분리하는 명명 규칙을 갖는다. DNS의 검색기능은 nslookup 프로그램을 이용해서 커맨드라인으로부터 액세스가 가능하다.(물론, DNS 명명 서비스에 대한 API도 존재한다.) 컨텍스트는 대게 객체들을 바인딩하고 해제하는 방법과 그것들을 열거하는 방법을 제공한다.

때때로 명명 서비스 의 객체들은 다른 프로그램이 필요로 하는 데이터를 포함한다. 가령, J2EE애플리케이션에서 환경 변수를 나타내는 객체들은 대게 명명 서비스 에 저장된다. 하지만 이외에 명명 서비스의 객체는 객체에 대한 레퍼런스를 나타낸다. 예를 들면, 서버에 레퍼런스를 제공하는 객체는 통상적으로 오픈 서버 커넥션이 아닌 서버에 대한 레퍼런스로서 명명 서비스 에 의해 저장된다.명명 서비스 에 의해 리턴된 레퍼런스 객체는 필요시에 서버 커넥션을 생성하기 위해 사용될 수 있다.

다음 그림은 컨텍스트의 개념도이다. top 컨텍스트는 /top로 불리며 객체, 레퍼런스, 그리고 다른 컨텍스트들을 포함한다. 컨텍스트 /top은 subcontexts b 와 g를 갖는다. 컨텍스트 /top/g 는 subcontext "b"를 갖는다. A라고 불리는 객체가 하나 이상 일지라도, 특정 개체 /top/g/b/a는 그 위치가 컨텍스트 /top/g/b 임이 분명하기 때문에 찾을 수가 있다.

figure 2

바인딩된 객체들에 대한 데이터를 제공하는 명명 서비스를 디렉토리라고 부른다. 가령, 파일시스템 디렉토리는 일반적으로 크기, 타입, 접근 허용 그리고 파일을 생성하고 수정한 날짜에 관한 정보를 제공한다. 몇몇 디렉토리는 이름으로 검색, 애트리뷰트의 조합으로 검색 모두를 허용한다.

각각의 명명 서비스들은 각자의 태스크(task)에 잘 맞도록 되어있지만, 그들이 작동하는 방식은 서로 다르다. 각 명명 서비스는 고유의 명명규칙, 검색기능, 바인딩과 디렉토리 프로토콜(directory protocols)과 객체 서비스 인터페이스(object service interfaces)를 갖는다. JNDI 는 네트워크 서비스를 이름짓고 찾기 위해 일관된 방법을 제공한다.

Java Naming and Directory Interface

JDBC 데이터베이스 커넥션(database connections), JMS 큐(JMS queues) 혹은 엔터프라이즈 빈 홈 인터페이스(enterprise bean home interfaces)와 같은 네트워크 객체에 액세스하기 위해 JNDI 를 사용하는 방법을 이미 알고 있을 것이다. 사실 JNDI 는 이름들을 객체로 매핑하지만, JNDI 는 명명 서비스가 아니다. 그보다도 JNDI는 명명 서비스를 표준적인 방법으로 액세스가능하게 하면서 기존의 명명 서비스를 감추는(wrap) 인터페이스들의 집합이다.

다음 그림에서 보는 것과 같이, 자바 애플리케이션은 JNDI인터페이스를 이용해서 감춰진(underlying) 명명 서비스에 액세스한다.

figure 3

애플리케이션내의 코드는 JNDI 인터페이스 메소드를 호출한다. 이러한 메소드를 구현하는 객체들은 JNDI 인터페이스 호출을 감춰진 명명 서비스에 대한 호출로 매핑한다. 또한 JNDI는 통합된 명명 규칙도 정의한다. JNDI 이름들은 JNDI의 명명 관리자(naming manager)에 의해 감춰진 명명 서비스의 명명 규칙을 따르는 이름으로 매핑된다.

javax.naming 패키지는 다음과 같은 명명과 디렉토리에 관련된 인터페이스들을 포함한다.

  • javax.naming.Context는 컨텍스트를 나타내는데, 이것은 바인딩과 서브컨텍스트를 찾고 관리하는 데에 쓰인다.
  • javax.naming.Name는 명명 서비스의 이름을 추상적으로 표현(abstract representation)하게 해준다.
  • javax.naming.Binding은 명명 서비스 이름과 그 이름에 바인딩된 객체의 표현이다.
  • javax.naming.Reference는 객체의 복사본을 얻어낼 수 있게 해준다.

컨텍스트 찾기

이 팁에 포함된 샘플코드는 JNDI컨텍스트의 컨텐츠를 열거하는 방법을 보여준다. 샘플 서블릿 Oct2003Servlet는 사용자가 입력한 이름에 해당하는 JNDI namespace내의 컨텐츠를 찾고 디스플레이한다.

컨텍스트를 얻는 가장 쉬운 방법은 javax.naming.InitialContext 클래스의 인스턴스를 생성하는 것이다. 샘플 서블릿 메소드 jndiList 는 최초의 컨텍스트를 생성하고 명명된 객체를 찾을 때 그것을 사용한다.

   InitialContext ic = new InitialContext();
   Object objFound = ic.lookup(name);

여기에서 name은 사용자가 HTML페이지에서 입력한 HTTP GET 혹은 POST 변수명(vriable name)이다. 만약 리턴된 객체가 Context 라면 jndiListlistContext 메소드를 호출하고, ListContext메소드는 주어진 이름에 해당하는 컨텍스트의 컨텐츠를 열거한다. 객체가 DataSource이면, jndiList 는 명명된 데이터 소스에 관한 정보를 출력한다.

listContext 메소드는 주어진 JNDI 컨텍스트의 컨텐츠를 하나의 테이블로 출력한다. 이를 위해서는 Context 메소드 listBindings 를 이용하는데, 이는 NamingEnumeration 를 리턴한다.

      NamingEnumeration ne = context.listBindings("");

NamingEnumerationjava.util.Enumeration를 구현한 것이다. NamingEnumeration.next 메소드는 javax.naming.Binding 타입의 객체를 리턴하고, 이는 객체의 이름과 객체의 클래스 이름 그리고 저장된 객체 자체를 포함한다.

      
      while (ne.hasMore()) {
         Binding ncp = (Binding)ne.next();
         String objName = ncp.getName();
         String objClass = ncp.getClassName();
         Object objObj = ncp.getObject();

         ...
     }

단순히 Context내의 이름들과 클래스이름을 보고자 한다면 Context.list 메소드를 이용할 수 있다. Context.listNamingEnumeration를 리턴하지만, 그것이 담고있는 컬랙션은 Binding이 아닌 NameClassPair 타입이다. NameClassPair는 이름과 객체 클래스 이름만을 포함한다.

샘플애플리케이션을 배포하고 실행하는 방법은 샘플코드 실행하기 를 참고한다.

애플리케이션을 실행하면, 다음과 같은 시작페이지를 보게 된다.

jndichoice

컨텍스트를 입력하거나, 텍스트 필드를 빈 상태로 놔두고 명명된 컨텍스트의 컨텐츠를 보기 위해 List버튼을 클릭한다. 예를 들면 jdbc 의 엔트리는 다음과 같은 화면을 디스플레이한다.

jndilist

JNDI 에 관한 더 자세한 정보는 JNDI 튜토리얼를 참고한다.