J2EE 웹 애플리케이션 컴포넌트를 작성할 때, 애플리케이션을 배포하는 사람이 이를 유연하게 사용할 수 있도록 해야 할 때가 있다. 예를 들어서 애플리케이션을 여러가지 버전으로 제공함으로써 사용자가 그들의 요구에 맞는 것을 선택하여 사용하도록 할 수도 있다. 이때에 코드의 어떤 부분은 배포시에만 유효한 호스트 네임과 포트 번호를 필요로 할 것이다. 아니면 단순하게 데이터가 디스플레이 되는 방식에 유연성을 줄 수도 있다.

환경 엔트리(environment entries)를 이용하면 이러한 유연성을 제공할 수가 있다. 여기서 환경 엔트리란 컴포넌트의 deployment descriptor에 정의할 수 있는 파라미터(parameter)를 말한다. 애플리케이션 컴포넌트는 JNDI를 이용해서 이름으로 환경 엔트리들을 검색하고, 비헤비어나 프리젠테이션을 커스터마이징(customize)하기 위해서 이 값들을 이용한다.

환경 엔트리는 모든 타입의 애플리케이션 컴포넌트에서 이용이 가능하다. 서블릿, 엔터프라이즈 빈, JSP 페이지, 커스톰 태그 모두 환경 엔트리를 이용할 수 있다. 웹 컴포넌트는 web.xml, 엔터프라이즈 빈은 ejb-jar.xml처럼 엔트리는 컴포넌트의 타입에 따라 적절한 deployment descriptor로 정의되어야 한다.

가령, 전자 상거래 애플리케이션을 위한 서블릿을 작성하고 있다고 가정해보자. 서블릿은 주문을 받았다는 사실을 알리기 위해 고객에게 이메일을 보내는데, 이 때에 서블릿은 인증된 SMTP 서버의 호스트 네임, 포트 넘버, 로긴 네임, 패스워드를 필요로 한다. 이러한 정보는 컴포넌트(서블릿) 개발자가 아닌 배포자(deployer)만이 알고 있다. 하지만 이메일이 기능하기 위해서는 이러한 정보를 필요로 한다. 어떤 방법으로 이 정보를 서블릿에 제공할 수 있을까?

서블릿에 이를 제공하는 한가지 방법은 서블릿의 deployment descriptor인 web.xml내에 있는 환경 엔트리를 이용하는 것이다. 먼저, 호스트 네임, 포트 넘버, 로긴 네임, 패스워드를 위한 환경 엔트리를 정의하자. 그리고 JNDI를 이용하여 환경 엔트리로부터 이러한 값들을 받는 코드를 작성하고, 받은 값들을 코드에 사용한다. 배포시에, 배포자는 환경 엔트리에 적당한 값을 채워 넣기 위해 배포툴을 사용한다. 그러면 프로그램은 런타임시에 배포자가 정해놓은 값을 읽게 되고 이 값을 해당서버에 액세스하는데 사용한다.

환경 엔트리 정의하기

컴포넌트의 deployment descriptor에 XML으로 환경 엔트리를 정의하자. J2EE Reference Implementation에서 사용하는 deploytool program과 같은 배포툴(deployment tool)을 이용한다면, GUI를 통해 deployment descriptor를 작성할 수 있다. 하지만 텍스트 에디터를 이용해서 직접 deployment descriptor를 작성한다고 가정해 보자.

환경 엔트리는 4개의 부분으로 이루어져 있다.

  • Description. <description> 태그 내의 스트링
  • Name. <env-entry-name> 태그 내의 스트링
  • Value. <env-entry-value> 태그 내의 값
  • Classname. <env-entry-type> 태그 내 엔트리의 타입

설명(description)은 배포 툴 사용자 인터페이스(deploy tool user interface) 에서만 보여지는 선택적인 텍스트형식의 설명이다. 이는 환경 엔터티를 해석하기 위해 필요한 사항을 배포자에게 알려준다. 다시 말하면, 배포자에게 나머지 값들을 작성하는 방법을 알려주는 이해를 돕는 설명이다. 또한 엔트리가 선택적(optional)인지 아닌지를 알려주기도 한다.

env-entry-name는 JNDI 컨텍스트 이름(context name) "jndi:comp/env"과 관계 있는 이름으로 컴포넌트가 엔트리를 검색하기 위해 그 이름을 사용한다. 모든 환경 엔트리들은 각자의 컨테이너에 의해 JNDI 컨텍스트에 기록된다.

env-entry-value는 환경 엔트리가 반드시 가져야 하는 값이며 스트링 타입이다. 단일 캐릭터를 받는 java.lang.Character를 제외한, 환경 엔트리를 허용하는 모든 타입은 스트링을 받는 생성자를 갖는다. env-entry-value 태그는 값의 생성자를 위해 쓰이는 스트링도 포함한다.

env-entry-type은 환경 엔트리의 값이 어떤 타입인지를 나타내는 클래스 이름이다. 그 타입은 반드시 다음 값 중에 하나여야만 한다.

  • java.lang.Boolean
  • java.lang.Byte
  • java.lang.Character
  • java.lang.Double
  • java.lang.Float
  • java.lang.Integer
  • java.lang.Long
  • java.lang.Short
  • java.lang.String

SMTP 호스트의 예제를 위한 환경 엔트리는 다음과 같다.

   <env-entry>
     <description>
     Enter the host name for sending email
     </description>
     <env-entry-name>SMTP Host Name</env-entry-name>
     <env-entry-value>
     homer.springfield.ma.us
     </env-entry-value>
     <env-entry-type>java.lang.String</env-entry-type>
   </env-entry>

   <env-entry>
     <description>SMTP port number for email
     </description>
     <env-entry-name>SMTP Port</env-entry-name>
     <env-entry-value>2101</env-entry-value>
     <env-entry-type>java.lang.Integer</env-entry-type>
   </env-entry>

   <env-entry>
     <description>
     User authentication for SMTP server
     </description>
     <env-entry-name>SMTP User</env-entry-name>
     <env-entry-value>bart</env-entry-value>
     <env-entry-type>java.lang.String</env-entry-type>
   </env-entry>

   <env-entry>
     <description>
     Password for SMTP user
     </description>
     <env-entry-name>SMTP Password</env-entry-name>
     <env-entry-value>D'oh!</env-entry-value>
     <env-entry-type>java.lang.String</env-entry-type>
   </env-entry>

환경 엔트리 사용하기

코드 내에 환경 엔트리를 사용하고자 한다면, JNDI를 이용하여 간단히 엔트리를 검색하면 된다. Context.lookup 메소드의 결과를 적절한 타입으로 타입캐스팅(typecast)해야 한다는 것을 기억하자. 앞서 본 예제를 타입캐스팅한 결과를 보자.

   try {
     InitialContext ic = new InitialContext();
     Context ctx = ic.lookup("java:comp/env");
     String hostname = 
                     (String)(ctx.lookup("SMTP Host"));
     Integer port = (Integer)(ctx.lookup("SMTP Port"));
     String user = (String)(ctx.lookup("SMTP User"));
     String password = 
                 (String)(ctx.lookup("SMTP Password"));

     sendEmail(
            emailText, port, hostname, user, password);
   } catch (NamingException nex) {
     ...
   }

환경 엔트리 vs. 서블릿 초기화 파라미터

웹 애플리케이션에서 서블릿 특성은 환경 엔트리대신 서블릿 초기화 파라미터(servlet initialization parameters)를 이용해서 사용자 정의될 수 있다. 서블릿 개발자는 web.xml의 init-param를 이용해서 서블릿 초기화 파라미터를 정의하고, javax.servlet.GenericServlet.getInitParameter 메소드를 이용해서 서블릿 코드 내에서 그 파라미터에 액세스한다. 서블릿 초기화 파라미터의 스코프는 그것을 정의하는 서블릿에 제한적이다.

그렇다면 특정한 사용자정의를 하려고 할 때, 환경 엔트리와 서블릿 파라미터 중 어떤 것을 선택해야 할까? 정답은 사용자 정의의 자연스러운 영역이(natural scope) 어디까지냐에 따라 달려있다. 글로벌 변수들이 프로그램의 네임스페이스를 오염시키는 것과 같이 모든 사용자정의(customizations)를 환경 엔트리에 두는 것은 JNDI 네임스페이스를 어지럽힌다. 이는 컴포넌트간에 원치 않는 의존성을 갖게 하는 원인이 되기도 한다. 서블릿 초기화 파라미터는 사용자정의가 오직 하나의 서블릿에 영향을 끼칠 때 매우 효과적이므로 사용자 정의가 다중 컴포넌트와 연관될 때에는 환경 엔트리의 사용을 고려해 봐야 한다.

샘플 코드

이번 테크팁의 샘플 코드는 두개의 부분으로 되어 있다. 첫번째는 애플리케이션의 모든 환경 엔트리를 프린트하는 서블릿이다. 이 서블릿은 2003/10/27 테크팁"JNDI의 소개"에서 소개한 Context.listBindings 메소드를 이용하여 JNDI 컨텍스트,java:comp/env,내의 바인딩을 모두 열거함으로써 자신의 역할을 끝내게 된다. 다음은 서블릿으로부터 발췌한 코드이다.

   public void printEnvEntries(HttpServletRequest req,
                               HttpServletResponse res)
      throws IOException, ServletException {

      res.setContentType("text/html");
      PrintWriter out = res.getWriter();

      try {
         InitialContext ic = new InitialContext();

         NamingEnumeration ne = 
                      ic.listBindings("java:comp/env");
         
         out.println(
         "<HTML><HEAD><TITLE>Environment Entries</TITLE></HEAD>");
         out.println(
         "<BODY><TABLE BORDER=1><TR><TH>Entry</TH>" +
                     "<TH>Value</TH></TR>");
         
         while (ne.hasMore()) {
            Binding ncp = (Binding)ne.next();
            String objName = ncp.getName();
            Object objObj = ncp.getObject();
            
            out.println("<TR><TD>" + objName + "</TD>");
            out.print(
             "<TD>" + objObj.toString() + "</TD></TR>");
         }
         out.println("</TABLE></BODY></HTML>");
         
      } catch (Exception e) {
         throw new ServletException(e);
      }
   }

위의 메소드는 java:comp/env내에 있는 모든 객체가 담고 있는 엔트리 이름과 값의 텍스트 표현(text representation)을 테이블에 반복적으로 프린트한다. 애플리케이션을 배포하고 deployment descriptor에 정의된 엔트리를 살펴보자. 배포를 위한 설명은 "샘플코드 실행하기"를 참고한다.

이 팁의 두번째 샘플 코드는 커스텀 태그인 DateTag.java이다. 이 샘플은 컴포넌트(이 경우에는 커스텀 태그)를 사용자정의가 가능하게끔 만들기 위해 환경 엔트리를 사용하는 방법을 설명하는 주요한 예제이다.

DateTag는 페이지 개발자가 서버에서 날짜와 시간을 프린트하기 위해 사용하는 단순한 태그이다. "<t:date/>"과 같이 단독으로 쓰일 경우에는, 시간과 날짜를 표준 포맷으로 프린트한다. 하지만 태그의 포맷 애트리뷰트를 이용해서 새로운 포맷을 정의하면 표준 포맷대신 새롭게 정의된 포맷으로 프린트한다.(신텍스 포맷은 SimpleDateFormat 표준 클래스에 의해 정의된다.)

배포자는 기호화된 이름(symbolic names)으로 시간/날짜 포맷의 리스트를 정의하기 위해 환경 엔트리를 이용할 수 있다. DateTag의 포맷 애트리뷰트의 값이 $로 시작한다면, 태그는 애트리뷰트에서 지정한 환경 엔트리의 값에서 그 포맷을 찾는다. 다음 환경 엔트리는 web.xml에서 발생한다.

   <env-entry>
     <env-entry-name>LongTimeDateFormat</env-entry-name>
     <env-entry-value>
     'Date:' EEEE, d MMMM yyyy', Time:' kk:mm:ss z
     </env-entry-value>
     <env-entry-type>java.lang.String</env-entry-type>
   </env-entry>

샘플 JSP 페이지는 다음 텍스트를 포함한다.

   The server date in "Obtuse" format is
     <mytags:date format="$ObtuseTimeDateFormat"/>.

이것은 런타임시에 다음과 같이 번역된다.

  The server date in "Obtuse" format is 
  20030511-23:05:04EST.

이는 배포자가 애플리케이션 deployment descriptor에 흔히 사용되는 날짜 포맷의 리스트를 정의할 수 있다는 것을 의미한다. 애플리케이션 개발자는 DateTag를 이용하여 JSP 페이지에 날짜 포맷을 참조하게 할 수 있다. 만약 deployment descriptor에 정의된 포맷을 변경하면, 애플리케이션의 모든 날짜/시간 값의 형태도 변경된다.

이것이 어떻게 작동되는 것일까? DateTag.setFormat메소드는 $로 시작하는 포맷이 있는지를 체크하고 만약 있다면, 그에 대응하는 환경 엔트리를 로드한다.

   public void setFormat(String sFormat) {
      if (sFormat.length() > 0 &&
          sFormat.charAt(0) == '$') {

         // Remove $
         sFormat = sFormat.substring(1);

         // Look up environment entry
         String format = null;
         try {
            InitialContext ic = new InitialContext();
            format = (String)ic.lookup(
                          "java:comp/env/" + sFormat);
         } catch (NamingException nex) {
            format = "[" + nex.toString() + "]";
         }
         
         // If we found something
         if (format != null) {
            sFormat = format;
         } else {
            sFormat = 
             "[" + sFormat + ": environment entry not set]";
         }
      }
     _sFormat = sFormat;
   }

샘플코드를 배포하고 실행하자. 배포를 위한 설명은 "샘플코드 실행하기"를 참고하기 바란다. 자기자신만의 고유한 포맷을 정의하고 애플리케이션의 JSP 페이지에 그것을 사용해 보자.

한단계 높은 수준의 실습을 원한다면, 국제화(internationalization)를 지원하는 태그를 포함시켜 볼 수 있다. Java BluePrints 프로그램의 Java Pet Store Sample Applications 내에 있는 Data Access Objects가 데이터 리소스를 국제화하는 방법을 보여준다.

"Java EE" 카테고리의 다른 글

2003/12/24 14:57 2003/12/24 14:57

TRACKBACK :: http://blog.sdnkorea.com/blog/trackback/270

댓글을 달아 주세요

  1. 박기형  수정/삭제  댓글쓰기

    이런 식으로 하면 더욱 편하고 효율적이겠네요.

    2007/09/19 02:44
  2. 박정숙  수정/삭제  댓글쓰기

    좋은 정보 감사해요~

    2007/09/19 05:52
[로그인][오픈아이디란?]

◀ Prev 1  ... 600 601 602 603 604 605 606 607 608  ... 626  Next ▶