본문 바로가기

tomcat

Tomcat에서 JNDI 설정


참고 URL : http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html
이 글에 대한 해석 + 제가 해보고 알게 된 부분을 추가한 글입니다.

보통 DataSource를 가져오기 위해서 많이 사용합니다..
하지만, 꼭 DataSource일 필요는 없습니다.

우선 사용하기 위한 DataSource를 추가하여야 합니다.

Server.xml에 설정하면 모든 application이 바라 볼 수 있을 것이고
META-INF/context.xml이나 conf/engineName/host/context.xml 이쪽에 설정하거나 아무튼
context에 한정지어서 설정하면 해당되는 application만 그 리소스를 받아 볼 수 있을 것입니다.

lookup을 하는 방법은 InitialContext 를 이용합니다. 모든 설정된 resources들은 JNDI 네임스페이스의
 java:com/env 부분으로 들어가게 됩니다.

따라서 리소스에 접근하는 일반적인 방법은

Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");

envCtx.lookup(name);

이런 방법으로 접근이 가능합니다.

우선 데이터소스가 아니라 일반적인 Pojo를 사용해보겠습니다.

1. JavaBean 작성.

import com.tistory.devyongsik;

public class MyBean {

  private String foo = "Default Foo";

  public String getFoo() {
    return (this.foo);
  }

  public void setFoo(String foo) {
    this.foo = foo;
  }

  private int bar = 0;

  public int getBar() {
    return (this.bar);
  }

  public void setBar(int bar) {
    this.bar = bar;
  }
}

이 자바빈을 jndi를 통해서 얻어오려고 합니다.
이 클래스는 리소스 팩토리가 lookup 할 때 초기화 됩니다.


2. 이제 이 데이터 소스를 어플리케이션에서 바라 볼 수 있도록 배포 관리자(WEB-INF/web,xml)에 정의를 해줘야 합니다.

<resource-env-ref>
  <description>
    Object factory for MyBean instances.
  </description>
  <resource-env-ref-name>
    bean/MyBeanFactory
  </resource-env-ref-name>
  <resource-env-ref-type>
    com.tistory.devyongsik.MyBean
  </resource-env-ref-type>
</resource-env-ref>

<resource-ref>를 사용하셔도 됩니다. 
<resource-ref>는 resource manager connection factory obejct들을 
위한 것으로 가장 일반적인 예가 javax.sql.Connection을 얻을 수 있는 javax.sql.DataSource 클래스입니다.
<resource-env-ref>는 Servlet 2.4부터 새롭게 추가된 resource-ref의 변형입니다. 
이것은 administered objects with resources 를 위한 것으로 가장 명백한 예제는 
JMS destination을 위해 사용하는 것입니다.
resoure-ref의 name이 resource factory를 위한 것이라면 
resource-env-ref의 name은 resource의 이름입니다.
 
더 자세한 내용은 http://www.coderanch.com/t/157993/EJB-Certification-SCBCD/certification/Difference-between-resource-ref-resource
이곳을 참조하시구요.. 아무튼 여기서는 resource-env-ref를 사용하겠습니다.
 
3. 이제 저 리소스를 사용 하는 코드를 작성해봅니다.
 
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory");
writer.println("foo = " + bean.getFoo() + ", bar = " + bean.getBar());
 
4. 이제 톰캣의 리소스 팩토리를 설정해 보겠습니다.
<Context> 엘리먼트 안에서 설정하면 됩니다. 혹은 Server.xml의 글로벌리소스에서..
  <Resource name="bean/MyBeanFactory" auth="Container"
            type="com.tistory.devyongsik.MyBean"
            factory="org.apache.naming.factory.BeanFactory"
            bar="23"/>



org.apache.naming.factory.BeanFactory 은 톰캣의 기본 라이브러리로 추가되어 있습니다.
(Catalina.jar)
그런데 이클립스에서는 위 리소스 설정을 META-INF/context.xml에 넣어줘야 인식을 하더라구요...
퍼블리싱 과정에서 뭔가 문제가 있는건지..--ㅋ
bean/MyBeanFactory가 logical한 이름으로 매칭되고 있다는 것을 확인하세요..

이렇게 하면 jndi를 통해 resource를 사용 할 수 있습니다.
jdbc 설정이라면.. web.xml에

<resource-ref>
<description> Resource </description> <res-ref-name> jdbc/EmployeeDB </res-ref-name> <res-type> javax.sql.DataSource </res-type>
<res-auth> Container </res-auth> </resource-ref>
 
이렇게 설정을 하고 (resource-ref를 사용했네요..)
server.xml이나 context.xml에
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource" username="dbusername" password="dbpassword"
driverClassName="org.hsql.jdbcDriver" url="jdbc:oracle:thin...." maxActive="8" maxIdle="4"/>
이렇게 DataSource에 대한 설정을 해주고 사용하면 됩니다. Lookup하는 방법은 위와 같습니다.
DataSource를 얻어와서 DataSource.getConntection()으로 Connection을 얻어와서 사용하면 됩니다.
마찬가지로 jdbc/EmployeeDB로 연결되어 있습니다.
톰캣의 표준 data source resource factory는
org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory 입니다.

여기서 이 factory 클래스가 spi의 역할을 하는데..
각 벤더별로 이 factory를 구현해서 나름대로의 jndi 서비스를 제공 할 수 있도록
하고 있는 것 같습니다..
사부님이 작성하셨던 코드를 보니까 일반 어플리케이션에서 이 데이터 소스와 dbcp를 jndi를 통해 얻어와서
작업을 하도록 해놓으셨더군요.. 나중에 이 어플리케이션을 web으로 올리더라도 다른 설정을 건드릴
것이 없이 지금 jndi에서 lookup하고 있는 이름으로 was에 jndi로 datasource를 설정만 하면
손쉽게 connection을 받아 올 수 있겠죠.. dbcp도 사용이 가능하구요.
 
맨 위에 링크를 걸어놓은 페이지에 가보시면 커스텀 팩토리도 만들 수 있습니다.
ObjectFactory 인터페이스를 구현하면 되는데 , 이것을 사용하시려면 팩토리를 작성하신
후 위 설정에서
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;

public class MyBeanFactory implements ObjectFactory {

public Object getObjectInstance(Object obj,
Name name, Context nameCtx, Hashtable environment)
throws NamingException {

// Acquire an instance of our specified bean class
MyBean bean = new MyBean();

// Customize the bean properties from our attributes
Reference ref = (Reference) obj;
Enumeration addrs = ref.getAll();
while (addrs.hasMoreElements()) {
RefAddr addr = (RefAddr) addrs.nextElement();
String name = addr.getType();
String value = (String) addr.getContent();
if (name.equals("foo")) {
bean.setFoo(value);
} else if (name.equals("bar")) {
try {
bean.setBar(Integer.parseInt(value));
} catch (NumberFormatException e) {
throw new NamingException("Invalid 'bar' value " + value);
}
}
}

// Return the customized instance
return (bean);

}

}

  <Resource name="bean/MyBeanFactory" auth="Container"
            type="com.tistory.devyongsik.MyBean"
            factory="org.apache.naming.factory.BeanFactory"
            bar="23"/>
이 부분의 factory를 바꿔주면 됩니다.
오늘도 풍대리님께 정말 너무나도 피가되고 살이 되는 강의를 들었습니다.
까먹을까봐 무섭네요...;;;