출처:http://network.hanbitbook.co.kr/view_news.htm?serial=565

 

Log4J

Log4J 의 문서에도 있듯이, log4j 객체를 생성해서 main 메소드가 있는 클래스에 public static으로 선언해 놓고 사용하는 것이 가장 보편적이다. 우선적으로 log4j에는 기본적인 3가지 Logger, Appender, Layout 클래스가 있는데 각 클래스들의 연관관계는 아래와 같이 간단하게 이야기할 수 있다.

"Logger 는 Appender 가 지정한 곳에 Layout 형식으로 로그를 기록한다"
Logger

Log4j 패키지의 핵심클래스이다. 설정을 제외한 대부분의 로깅작업을 수행한다. 로깅작업은 Category에 있는 debug, info, warn, error, fatal의 다섯 가지 메소드를 이용해서 수행할 수 있다. Log4j의 최신문서를 보면 Logger의 parent class인 Category는 deprecated 되었고 Logger 클래스로 대치되었으며 호환성을 위해 2003년 중반까지는 API에서 제거하지 않는다고 되어 있으나 앞으로 없어질 클래스이므로 Category를 사용하지 말고 Logger 클래스의 메소드들을 사용하기 바란다. (이전에 나온, 혹은 최근에 나온 몇몇 how-to 문서들을 보면 여전히 Category를 쓰는 코드를 볼 수 있는데 빠른 시일 내에 모두 수정되어야 할 것이다)

로그레벨 지정하기

앞에서도 언급했듯이 log4j에서는 기본적으로 debug, info, warn, error, fatal의 다섯 가지 로그레벨이 있다. 각각은 메소드 debug(), info(), warn(), error(), fatal()라는 5가지 메소드를 이용해서 로그를 남길 수 있다. 다만 이때 Logger의 setLevel에서 지정된 로그레벨이 있다면 지정된 로그레벨 이하의 로깅이벤트는 무시된다. 따라서 로그도 남지 않는다. 즉, 아래와 같이
logger.setLevel(Level.INFO); 
코드내에 지정되어 있다면, 다음의 세 코드 중
logger.debug("debug 로그");logger.info("info 로그");logger.warn("warning 로그");
debug 로그는 남지 않고 info 와 warn 로그만 남는다. 자바에서는 C와 같이 전처리기의 기능이 없기 때문에 #ifdef DEBUG와 같은 형태와 같이 디버깅 때와 릴리즈 때의 디버깅코드를 각각 별도로 생성할 수가 없다. 따라서 log4j의 이러한 기능은 로그관리에 있어서 상당히 편리하다.

Appender

log4j 의 api를 보면 실로 다양한 로그방식을 지원한다. 가장 단순한 Console부터 시작해서 파일, SMTP, 기타 등등… 의 방식들을 지원하는데 이들은 log4j 객체에 Appender의 객체로서 할당된다.
  • ConsoleAppender: 콘솔화면으로 출력하기 위한 appender이다.
  • FileAppender: FileAppender는 로깅을 파일에 하고 싶을 때 사용한다.
  • RollingFileAppender: FileAppender는 지정한 파일에 로그가 계속 남으므로 한 파일의 크기가 지나치게 커질 수 있으며, 계획적인 로그관리가 불가능해진다. RollingFileAppender는 파일의 크기 또는 파일백업인덱스 등의 지정을 통해서 특정크기 이상 파일의 크기가 커지게 되면 기존파일을 백업파일로 바꾸고, 다시 처음부터 로깅을 시작한다.
  • DailyRollingFileAppender: 설정한 날짜 또는 조건에 맞춰 로깅을 수행한다. 생성자 DailyRollingFileAppender(Layout layout, String filename, String datePattern);를 이용해서 객체의 생성과 함께 log가 roll 되는 시간을 지정해줄 수도 있고 기본 생성자로 생성 후 setDatePattern()을 이용해서 지정해 줄 수도 있다. DatePattern의 몇 가지 포맷은 다음과 같다.
    '.'yyyy-MM: 매달 첫번째날에 로그파일을 변경한다.'.'yyyy-ww: 매주의 시작싱 로그파일을 변경한다.'.'yyyy-MM-dd: 매일 자정에 로그파일을 변경한다. (필자는 주로 이 옵션을 이용한다)'.'yyyy-MM-dd-a: 자정과 정오에 로그파일을 변경한다.'.'yyyy-MM-dd-HH: 매 시간의 시작마다 로그파일을 변경한다.'.'yyyy-MM-dd-HH-mm: 매분마다 로그파일을 변경한다.
    보다 자세한 내용은 아파치 api 문서를 참고하기 바란다.
Layout

로그를 어떤 포맷의 형태로 남길 것인가? 단순히 메시지 외에도 현재 로그하는 대상의 스레드명, 로그시간 등등 많은 정보를 조합할 수 있다. Layout에는 HTMLLayout, PatternLayout, SimpleLayout, XMLLayout 등이 있다. SimpleLayout과 XMLLayout 등도 많이 사용할 수 있겠지만, 아무래도 자신이 원하는 스타일의 로그메세지를 남기기 불편한 면이 있다. 필자가 자주 사용하는 레이아웃은 PatternLayout으로서, c 함수의 printf처럼 다양한 로그 메시지 조합을 만들어 낼 수가 있다.

%p : debug, info, warn, error, fatal 등의 priority 가 출력된다.
%m : debug(), info(), warn(), error(), fatal() 등의 함수로 지정한 로그내용이 출력된다.
%d : 로깅 이벤트가 발생한 시간을 기록한다. 출력포멧은 %d후의 브레이스내에 지정된 형태를 따른다. %d{HH:mm:ss, SSS}라든가 %d{yyyy MMM dd HH:mm:ss, SSS}와 같은 형태로 사용하면 된다. Java 의 SimpleDateFormat 의 형식대로 사용하면 된다.
%t : 로그이벤트가 발생된 쓰레드의 이름이 출력된다.
%% : % 표시를 출력하기 위해 사용한다.
%n : 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다.


기타 자세한 내용은 log4j 홈페이지 또는 api 문서를 참고하길 바란다.

예제

다음은 log4j를 활용하는 간단한 예이다. 별도의 configuration 파일을 이용해서 log4j의 값을 세팅하는 방법도 있으나 지면 관계상 실행가능한 간단한 예제 하나로 대신할까 한다.

아래 예제는 DailyRollingFileAppender를 이용해서 분당 하나의 로깅파일을 만들어내도록 했으며, 5초마다 Hello, DEBUG와 Hello, INFO 그리고 Hello, WARN을 출력하도록 되어 있다. 그리고 화면으로도 로그결과를 확인하기 위해서 ConsoleAppender도 추가하였다. 기본적인 priority 모드는 INFO로 설정되어 있다. 각각 어떤 값들이 로그에 남고 DailyRollingFileAppender가 어떻게 동작하는지, 그리고 각각의 파라미터들을 어떻게 세팅하였는지 등을 중심으로 코드를 보기 바란다.

Log4j 역시 다운로드받은 후 ant를 이용해서 빌드해서 사용해야 한다.
import java.io.*;import org.apache.log4j.*;public class Test {    public Test(){        initLog();    }        public void initLog(){        /////////////////////////////////////////        // log4j 설정        logger.setLevel(Level.INFO);        DailyRollingFileAppender appender = null;        Appender consoleAppender = null;                try {            String logPattern = "%r [%t] %-5p %c %x - %m %d{yyyy MMM dd HH:mm:ss, SSS} \n";            appender = new DailyRollingFileAppender( new PatternLayout(logPattern), "syslog.log", "'.'yyyy-MM-dd-HH-mm" );            consoleAppender = new ConsoleAppender(new PatternLayout(logPattern));        }        catch(IOException e){            e.printStackTrace();            System.out.println("로그설정에 문제가 있습니다. 실행권한 체크등을 해보길 바랍니다.");            System.exit(-1);        }                logger.addAppender(appender);        logger.addAppender(consoleAppender);        logger = Logger.getRootLogger();                logger.debug("LBSGateway 객체 생성");        /////////////////////////////////////////    }        public static Logger logger = Logger.getRootLogger();        public void go(){        for ( int I = 0; I < 3; I++)            new testThread().start();    }        public static void main(String[] args){        Test t = new Test();        t.go();    }}class testThread extends Thread {    public void run(){        while ( true )            {Test.logger.debug("Hello, DEBUG");            Test.logger.info("Hello, INFO");            Test.logger.warn("Hello, WARN");                        Thread.sleep(5000);        }    }}
참고자료

Log4J 프로젝트 홈페이지는 http://jakarta.apache.org/log4j이다.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:22

출처 : http://eclipse.new21.org/phpBB2/viewtopic.php?t=146

 

본론으로 들어가자.
1. log4j를 웹로직에서 쓰자.

웹로직에서의 log4j를 쓰는 방법에 대해서 많이 생각을 해봤다.
좀처럼 쉽지 않았음 T.T
원리는 간단한데 말이다.
일단 log4j의 특성을 알아야 한다.
이 log를 적기 위해 별별 opensoure를 뒤져야 했다.
그 결론을 나름대로 내려보면

1. jdk1.4의 logging 기능을 함께 쓰는방법
2. apache common의 project를 이용해서 섞어쓰는 방법
3. log4j의 특성만 이용해서 사용하는 방법
위의 경우의 수중 내가 선택한방법은 3번이다.
그럼 3번을 쓰는 방법을 알아보자.



2. 환경설정

1. C:\bea\weblogic700\common\lib 아래에 log4j.jar 를 복사해 넣는다.
2. C:\bea\user_projects\mydomain 에서
set JAVA_OPTIONS=-Dlog4j.config=log4j.xml -Dweblogic.security.SSL.trustedCAKeyStore=C:\bea2\weblogic700\server\lib\cacerts 와 같이 JAVA_OPTIONS에 추가를 한다.
3. 이로서 환경 설정은 끝이다.
참고 : 만일 log4j.config파일을 사용하려면 위의 2의 Dlog4j.config=log4j.xml 을 Dlog4j.config=log4j.config 로 수정만 하면 된다.
이 예제에서는 xml를 사용하기로 했으니..xml예제를 한개 올린다.

log4j.xml
코드:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

   <appender name="cad" class="org.apache.log4j.RollingFileAppender">
      <!--<param name="File"   value="Logging4J.xml" />-->
      <param name="File"   value="Logging4J.log" />
      <param name="Append" value="true" />       
      <param name="MaxFileSize" value="1000"/>
      <param name="MaxBackupIndex" value="3"/>
     
     
      <!--<layout class="org.apache.log4j.xml.XMLLayout">
      </layout>-->
     
      <layout class="org.apache.log4j.PatternLayout">
         <param name="ConversionPattern"
                value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
      </layout>           
       
   </appender>

   <root>
      <priority value ="debug" />
      <appender-ref ref="cad"/>
   </root>

</log4j:configuration>




3. 헉 정말로 저게 끝이단 말인가?

그져 코딩에(jsp, servlet, ejb 등등...) 아래의 예처럼 코딩만하면 된다.

코드:
package org.new21.lovelazur.test;

import org.apache.log4j.Logger;

public class LoveLazurLoggerTest
{
  private static Logger logger =
    Logger.getLogger(LoveLazurLoggerTest.class.getName());

  public void logerTest()
  {
      logger.debug("test logger ... best log system log4j!!");
  }

}



4. web.xml의 수정
환경 설정이 이것 뿐이라는 말은 t.t
하나 더 있기는 하다.
우선 web.xml을 열어 이렇게 수정한다.
코드:
<?xml version="1.0" ?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>

  <!-- Loggin configuration -->
  <servlet>
    <servlet-name>Log4jInit</servlet-name>
    <servlet-class>org.new21.lovelazur.conf.Log4jInit</servlet-class>
    <init-param>
      <param-name>debug</param-name>
      <param-value>0</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

</web-app>


Twisted Evil Twisted Evil Twisted Evil
이건 뭐란말인가?
이것의 정체를 알려면 이전에 log에서 DomConfigurator를 찾아보기 바란다.
이것은 properties를 메모리에 로딩해서 범용적으로 쓰기 위함이다.
이제 이것의 servlet을 보도록 하자.

4. Config용 Servlet작성
org.new21.lovelazur.conf.Log4jInit

코드:
package org.new21.lovelazur.conf;

import javax.servlet.http.HttpServlet;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.xml.DOMConfigurator;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * <p>Controls Log4j logging level.  Implemented as a servlet so
 * that logging levels can be adjusted by redeploying the webapp.
 * Log file is determined by "log4j.config" set as a system
 * property in the server startup file.</p>
 *
 * @author Copyright (c) 2003 by BEA Systems. All Rights Reserved.
 */
public class Log4jInit extends HttpServlet {

  public void init() {

    int debug = 0;
    String value;
    String logFile;

    logFile = System.getProperty("log4j.config");
    System.out.println("############### Log4J log file ##############" + logFile);
    value = getServletConfig().getInitParameter("debug");

    try {
       debug = Integer.parseInt(value);
    } catch (Throwable t) {
        debug = 0;
    }

    if (debug >= 1) {
      SimpleDateFormat formatter = new SimpleDateFormat("MMM d, yyyy H:mm:ss a z");
      Date today = new Date();
      String output = "<"+formatter.format(today)+"> <Debug> <love lazur>";
      System.out.println(output+" love lazur app log4j prop file: "+logFile);
    }

   
    if(logFile != null) {
      if (logFile.toString().toLowerCase().endsWith(".xml")) {
         System.out.println("############### Log4J DOMConfigurator configure ##############" );
         DOMConfigurator.configure(logFile);
      } else {
         System.out.println("############### Log4JPropertyConfigurator configure ##############" );   
         PropertyConfigurator.configure(logFile);
      }
    }
   
  }
}


이것을 compile해서 WEB-INF 아래에 넣어주면 된다. Twisted Evil Twisted Evil Twisted Evil Twisted Evil Twisted Evil


5. log확인
이것을 잘 따라했다면...아래와 같은 로그를 볼수 있다.
로그 파일 이름은 log4j.xml에 기술한 Logging4J.log 이다. 그리고 이 로그는 주기적으로 backup을 받으므로 착오없길 바란다.
2004-02-26 13:31:29,578 DEBUG [ExecuteThread: '11' for queue: 'default'] test.LoveLazurLoggerTest (LoveLazurLoggerTest.java:12) - test logger ... best log system log4j!!
2004-02-26 13:31:29,609 DEBUG [ExecuteThread: '11' for queue: 'default'] test.LoveLazurLoggerTest (LoveLazurLoggerTest.java:12) - test logger ... best log system log4j!!
2004-02-26 13:31:29,640 DEBUG [ExecuteThread: '11' for queue: 'default'] test.LoveLazurLoggerTest (LoveLazurLoggerTest.java:12) - test logger ... best log system log4j!!
2004-02-26 14:05:06,656 DEBUG [ExecuteThread: '12' for queue: 'default'] test.LoveLazurLoggerTest (LoveLazurLoggerTest.java:12) - test logger ... best log system log4j!!
2004-02-26 15:10:03,453 DEBUG [ExecuteThread: '12' for queue: 'default'] calculation.BonusCalculatorBean (BonusCalculatorBean.java:21) - <<<<<<< test EJB logger ... best log system log4j!! >>>>>>>



LOG4J의 Weblogic에서의 설정을 마치고 사용법도 익혔다.

만일 tomcat및 다른 container에서 사용하려면 class path(tomcat의 경우는 xxx/yyy/lib 인데 기억안나지만 그곳에 log4j.jar를 넣어두면 된다.
그리고 web.xml및 Log4jInit.class 을 적절한 위치에 넣어두면 된다.
아마 다 될것이라고 믿는다.
jsp의 경우 로그 프로그램 코딩을 넣는게 부담스럽기도 하다.
이런경우 custom tag를 이용해 간단하게 작성, 사용하는게 바람직할것으로 본다.
차후 시간이 나면 jsp custom tag도 만들어 볼것이다.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:22
[첨 언] : 이 문서는 JavaWorld의 문서를 번역한 것입니다. 따라서 라이센스는 
아마도 JavaWorld와 원저작자가 가지고 있지 않을까 싶습니다. 편의상 경어는 
생략합니다. [번 역] : 최 호필(hops) 
E-mail : hops@bcline.com Homepage : http://hops.x-y.net 
[번역일] : 2003년 4월 30일 
[원 문] : http://www.javaworld.com/javaworld/jw-12-2001/jw-1228-velocity_p.html  
Start up Velocity Template Engine 
[참 고] : 모든 예제는 J2SE v1.4.1_01, Velocity v1.3에서 테스트되었습니다. 
 

Velocity Template Engine 시작하기

자바 기반의 웹 컨텐트 생성도구, Open Source Velocity

Summary
Velocity Template Engine은 여러분들이 어플리케이션이나 서블릿 내에서 데이터를 표현하게 만들어 준다. 주로 서블릿 기반의 웹사이트들과 같은 다이나믹한 개발에 사용되는데 Velocity의 템플릿과 자바 코드간의 깔끔한 분리는 모델 2 스타일 Model-View-Controller(MVC) 웹 개발에 대하여 이상적으로 만들어 줄 수 있다. 일반적인 템플릿 엔진들처럼 Velocity는 코드생성, XML 생성과 변환, 그리고 텍스트 스트림 프로세싱과 같은 많은 다른 목적들에도 적합하다. 이 아티클은 Velocity Template Language(VTL)을 소개하고 Velocity엔진에서 어떻게 사용하는지 예제를 제공한다. 또한 자바 서블릿 환경에서 어떻게 웹 컨텐트를 생성하는지도 포함한다.(3,000 글자정도; 2001년 12월 28일)
Geir Magnusson Jr. 작성


Velocity는 국제적으로 자발적 참여 커뮤니티에 의해 개발된 오픈소스 템플릿 도구이고, Apache Software Foundation의 Jakarta Project에 의해 주관된다. 무료로 이용가능한 소스코드를 다운받을 수 있는 웹사이트인 Jakarta Velocity Project에 속해 있는 사용자들이 질문에 대한 대답들을 해 줄 준비가 되어 있고 일반적인 템플릿 문제들에 대한 솔루션들을 제공해 줄 준비가 되어있다. Velocity는 WebMacro 프로젝트로부터 영감을 받았다.

이 아티클에서 Velocity Template Engine과 템플릿 랭귀지인 Velocity Template Language(VTL)에 대한 간략한 입문서를 제공한다. 또한 몇가지 예제를 통해서 어떤식으로 Velocity를 사용하는지 데모를 보여줄 것이다.

당연히 Hello World
아무런 설명도 필요없는 프로그래밍 관련 주제는 Hello World 예제외에는 없을 것이다. Velocity에서 사용하는 어떠한 어플리케이션이라도 두 부분을 필요로 한다. 첫번째는 템플릿으로써, 이 예제는 아래에 있다. 이 파일명은 helloworld.vm이 된다.


  Hello $name!  Welcome to Velocity!

두번째는 HelloWorld.java로 명명된 위의 템플릿에 대응되는 자바 프로그램이다.


import java.io.StringWriter;

import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;


public class HelloWorld
{
    public static void main( String[] args )
        throws Exception
    {
        /*  먼저 엔진을 얻고 초기화를 한다.  */

        VelocityEngine ve = new VelocityEngine();
        ve.init();

        /*  다음으로 템플릿을 얻어낸다.  */

        Template t = ve.getTemplate( "helloworld.vm" );

        /*  컨텍스트를 생성하고 데이터를 추가한다. */

        VelocityContext context = new VelocityContext();

        context.put("name", "World");

        /* 이제 StringWriter 안으로 템플릿을 표현한다. */

        StringWriter writer = new StringWriter();

        t.merge( context, writer );

        /* World를 보여준다. */

        System.out.println( writer.toString() );    
    }
}

이 프로그램을 컴파일하고 실행을 하면 아래의 결과를 볼 수 있다.
[역자 주]: Velocity 1.3의 경우 컴파일과 Run을 하기 위해서는 클래스패스에 velocity-dep-1.3.jar가 잡혀 있어야 한다.


  Hello World!  Welcome to Velocity!

이것은 사소한 예제이긴 하지만, Velocity templating은 어떤 것이라는 사상을 제공해 준다는 결정적인 부분을 포함하고 있다.

왜 이것을 사용해야 하는가?
일반적인 템플릿 도구들이 사용하기 쉽도록 디자인된 것과 같이 Velocity는 데이터를 formatting하고 표현할 수 있게 요구되는 어떠한 자바 어플리케이션 영역에서도 유용하게 사용된다. 여러분들은 다음과 같은 이유때문에 Velocity를 사용해야만 한다.

  • 다양한 어플리케이션 영역들에 적용된다.
  • 템플릿 디자이너에게 단순하고 명확한 신택스(syntax)를 제공한다.
  • 개발자에게 간단한 프로그래밍 모델을 제공한다.
  • 템플릿과 코드가 분리되기 때문에 서로 독립적으로 개발하고 유지보수 할 수 있다.
  • Velocity 엔진은 어떠한 자바 어플리케이션 환경, 특히 서블릿과 쉽게 통합된다.
  • Velocity는 템플릿이 컨텍스트 내에 있는 데이터 객체의 어떠한 public 메소드들에게 엑세스하는 것을 가능하게 만들어준다.

마지막 포인트는 매우 중요하다 -- 이것은 여러분들이 이미 존재하는 여러분들의 클래스를 재사용할 수 있다는 것을 의미한다. 따라서 여러분들의 템플릿에서 사용하길 원하는 객체들은 특정 방식, 예를 들어 자바빈즈나 아니면 특별한 I/O를 구현(implement) 한다던지, JSP(JavaServer Pages taglibs와 같은 lifecycle 방식으로 재구성되어질 필요가 없다. 단 하나의 필요사항은 메소드들이 public으로 선언되어야 한다는 것뿐이다. 우리가 자세하게 template language를 다룰때 이것에 대해서 좀 더 알아볼 수 있을 것이다.

Velocity의 장점 중의 한가지는 어플리케이션 내에서 기능적인 책임의 분리는 강력하게 강제화시키고 있다는 것이다. 이것은 템플릿이 어플리케이션 코드가 본질적으로 사용가능하게 만드는 객체들에 대해서 엑세스하는 것을 제한함으로써 수행된다. 이것은 Model-View-Controller(MVC) 개발에서 디자이너들이 배타적으로 데이터 표현(the view)에 촛점을 맞출 수 있도록 해 주고, 어플리케이션 프로그래머들은 어플리케이션 컨트롤(the controller)과 비즈니스 로직과 데이터 관리(the model)에만 촛점을 맞출 수 있도록 해 준다. MVC는 정교한 어플리케이션들의 개발과 지속적인 유지보수 두가지 모두를 단순화시키는 것이 잘 적용된 개발 패턴이다.

어느 곳에서 이것을 사용하는가?
Velocity는 아래와 같은 곳에서 성공적으로 사용될 수 있다.

  • 서블릿 기반의 웹 어플리케이션
  • 자바와 SQL 코드 생성(Generation)
  • XML processing과 transformation
  • RTF 파일 생성과 같은 Text processing

Velocity는 대다수 일반적으로 자바 서블릿 기반의 웹 어플리케이션 개발에 있어 JSP와 다른 렌더링 기술들과 결합 또는 대체하는 렌더링 엔진으로써 사용된다. 템플릿 syntax가 유지보수하기 쉽다는 것을 제외하고라도, Velocity의 template language는 조작하기 쉽고, 데이터를 생성하지 않고 데이터를 표현할 수 있기 때문에 웹 개발에서 사용된다. 이것은 템플릿 내에서 programming하는 것을 지양하게 만든다. 이것은 아주 좋은 것이다. 자바 코드의 비즈니스와 어플리케이션 로직이 포함되어야 할 곳에서만 존재하게 된다. J2EE(Java 2 Platform, Enterprise Edition) platform이 JSP보다 다른 output 기술들에 적합되어 있기 때문에, Velocity는 J2EE 웹 개발에 적합하다. 비록 JSP가 J2EE 스펙에 포함되어 있기는 하지만, J2EE는 JSP의 사용을 필요로 하지는 않는다.

어떻게 동작하는가?
어떠한 어플리케이션에서 하는 것처럼 Velocity 기반 어플리케이션을 생성하는데 똑같은 일반적인 프로세스를 사용한다. 이제는 위에 있는 Hello World 어플리케이션보다 좀 더 흥미있는 예제를 생각해 보자. 여러분들은 애완동물 가게를 운영하고 판매 홍보를 하기 위해 대량의 email을 생성해서 발송하길 원한다고 가정해 보자. 먼저 여러분들은 email을 다자인해야 한다. 그리고나서 그 디자인을 기반으로 해서 템플릿과 코드를 개발해야만 한다.

디자인시 고려할 문제들
디자인을 위해 3가지 항목을 고려해야 할 필요가 있다.

  • email 안에 포함되어야할 데이터는 어떤 것인가?
  • 데이터 항목은 어떤 형식을 가져야만 하는가?(예를 들어 list, Map 또는 String)
  • 무엇이 그러한 데이터 항목들을 호출하는가?

예를 들어, 각기 다르게 홍보된 가격을 가진 3가지의 애완동물을 판매하기로 결정했다고 가정해 보자. 여러분들은 각 애완동물 이름과 그 가격을 매칭시켜놓은 map을 사용하기로 결정하고 그런 다음에 list 안에 3개의 map을 모두 저장하기로 결정한다. 이 list를 petList로 호출하고, map 안의 애완동물 명을 name, 가격을 price란 이름으로 호출한다. 이제 여러분들은 관련된 데이터, 형식, 이름을 식별했기에 코드를 작성하고 템플릿을 디자인할 수 있다.

코드 작성과 템플릿 디자인
일단 여러분들이 데이터 명세에 동의한다면, Velocity는 여러분들이 코드를 작성하고 템플릿을 디자인하는 것을 병렬적으로 할 수 있도록 만들어준다. 디자이너는 템플릿 안의 nondata presentation content(image들, text 등과 같은)와 데이터를 통합시킨다. 이러한 경우 우리는 간단하게 email body를 아래처럼 작성한다.


  $petList.size() Pets on Sale!

  We are proud to offer these fine pets
  at these amazing prices.  This month only,
  choose from:

  #foreach( $pet in $petList )
    $pet.name for only $pet.price
  #end

   Call Today!

프로그래머로써 여러분들은:

  • 데이터 소스들 -- JDBC(Java Database Connectivity)를 통한 데이터베이스, 파일, 또는 그냥 계산된 어떤 값 -- 로부터 모든 데이터를 가져온다.
  • 약속된 이름들을 이용하여 컨텍스트 안으로 그 데이터를 넣는다.
  • 결과값을 만들기 위해 컨텍스를 가지고 템플릿을 렌더링한다.

여러분들은 내가 VelocityContext 클래스를 context으로 언급했던 Hello World 예제로부터 다시 생각해 볼 수 있을 것이다. java.util.Map로 형상화되었기에 컨텍스트는 템플릿이 엑세스하는 어플리케이션이나 서블릿에 의해 제공되는 데이터를 담고 있는 객체이다.

이 예제에서 우리는 우리의 데이터 소스들(여기서는 직접 코드에다가 넣었지만)로부터 모든 데이터를 얻고, 구성하고 컨텍스트에 추가한다.


   /* create our list of maps  */

   ArrayList list = new ArrayList();
   Map map = new HashMap();

   map.put("name", "horse");
   map.put("price", "$100.00");
   list.add( map );

   map = new HashMap();
   map.put("name", "dog");
   map.put("price", "$59.99");
   list.add( map );

   map = new HashMap();
   map.put("name", "bear");
   map.put("price", "$3.99");
   list.add( map );

   /*  add that list to a VelocityContext  */

   VelocityContext context = new VelocityContext();

   context.put("petList", list);

이제 컨텍스트 안에 구성되어서 위치된 데이터와 준비된 템플릿을 가지고 우리는 컨텍스트에 대해서 템플릿을 렌더링할 수 있다. 여기에 그 코드가 있다.


import java.io.StringWriter;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

public class PetStoreEmail
{
    public static void main( String[] args )
        throws Exception
    {
        /*  first, get and initialize an engine  */

        VelocityEngine ve = new VelocityEngine();
        ve.init();

        /*  organize our data  */

        ArrayList list = new ArrayList();
        Map map = new HashMap();

        map.put("name", "horse");
        map.put("price", "$100.00");
        list.add( map );

        map = new HashMap();
        map.put("name", "dog");
        map.put("price", "$59.99");
        list.add( map );

        map = new HashMap();
        map.put("name", "bear");
        map.put("price", "$3.99");
        list.add( map );

        /*  add that list to a VelocityContext  */

        VelocityContext context = new VelocityContext();
        context.put("petList", list);

        /*  get the Template  */

        Template t = ve.getTemplate( "petstoreemail.vm" );
        //[역자 주] 한글을 사용한다면 Template t = ve.getTemplate("petstoreemail.vm","KSC5601");을 사용한다.

        /*  now render the template into a Writer  */

        StringWriter writer = new StringWriter();

        t.merge( context, writer );

        /* use the output in your email body */

      sendEmail( writer.toString() );
    }
}

이 완성된 프로그램은 여러분들의 email body를 생성한다. Velocity는 Writer 안으로 template들을 표현하기 때문에, 여러분들은 손쉽게 결과물을 관리(manage)할 수 있다. 이 경우, 표현된 결과물은 StringWriter를 통해 String으로 보냈으나, 파일이나 browser, 또는 데이터베이스의 BLOB(binary large object)으로 손쉽게 보낼 수 있다. 이것이 바로 왜 Velocity가 자바 어플리케이션들과 그럽게 쉽게 통합될 수 있는가에 대한 이유들 중 하나이다.

프로그램의 output (여러분들의 email body)는 아래와 같이 된다.


  3 Pets on Sale!

  We are proud to offer these fine pets
  at these amazing prices.  This month only,
  choose from:

    horse for only $100.00
    dog for only $59.99
    bear for only $3.99

   Call Today!

Velocity Template Language
나는 두개의 다른 예제들을 통해 Velocity 템블릿들을 보여주었지만, 두 개의 케이스 모두 특별한 마크업이 무엇이었는지에 대해서는 아무런 설명을 하지 않았다(비록 여러분들이 아마도 추측했었겠지만).

Velocity Tempalte Language(VTL)은 두 파트를 제공하는 아주 간단한 문법이다. 컨텍스트 내에서 엑세스가 되는 객체들에 대한 형식인 references와 control과 action에 사용되는 문장들의 집합인 directives이다. "표준 비즈니스 카드에 충분히 맞추어져 있는 형태 집합을 가진 언어 정의"(Jim Jagielski의 "Getting Up to Speed with Velocity"를 참고하라)로 설명되었듯이 VTL은 커뮤니티에 의해 계획적으로 간결하고 작게 유지되어져 왔다.

References
템플릿 안의 References는 데이터를 엑세스한다. 이것들은 템플릿의 non-VTL 컨텐트와 자유롭게 섞인다. 형식적으로 정의된 것은, reference는 '$'문자로 시작하고 그리고 컨텍스트 내의 어떤 것을 참조하는 템플릿 내의 어떤 것이다. 만일 대응되는 데이터 객체가 컨텍스트 내에 존재하지 않는다면, 템플릿은 일반 텍스트로 reference를 취급하고 그것을 그대로 output stream에 보내서 표현한다.

여기에 non-VTL 컨텐트와 함께 섞여 있는 간단한 reference를 포함하고 있는 짧은 템플릿이 있다.


    Hello $name!  Welcome to Velocity!

여기에서 reference는 $name가 된다. Hello World 예제에서처럼, Velocity는 템플릿 안에 있는 $namename 키로써 컨텍스트에 존재하는 것의 toString() 리턴 값으로 대체한다.


    Hello World!  Welcome to Velocity!

Velocity 레퍼런스는 어떠한 객체의 public 메소드를 엑세스하는 것을 허용하게 만들어 주고, 템플릿의 syntax는 Java code 내에서 사용했던 것과 동일하게 되어 있다. 여기에 몇가지 예제들이 있다.


   There are $myBean.getSize() elements.

   $myObject.anotherMethod( 1, "more data ")

  
   $foo.getBar().barMethod("hello", $moredata )

  
   $foo.myMethod( $bar.callThis() )

여러분들은 아마 애완동물(Pet Store) email 예제로부터 다시 생각해 낼 수도 있을 것이다. 그 예제에서 우리는 java.util.Map에다가 이름(name)과 가격(price) 정보를 저장했었고, nameprice의 두 토큰을 사용해서 데이터를 접근했었다.


   $pet.name for only $pet.price

이러한 것은 Velocity가 JavaBean과 같이 introspection 메커니즘을 사용했기 때문에 동작하게 된다. 그렇게 됨으로써 property notation을 사용한 레퍼런스들로 메소드를 엑세스할 수 있도록 만들어 주게된다. 애완동물 예제 템플릿에서, Velocity의 introspection 기능은 nameprice의 키를 가지고 Mappublic get(String) 메소드를 찾고 호출(invoke)하게 만들어 준다. 우리는 템플릿 내에서 get(String) 메소드를 직접 호출하는 다른 방식을 사용해서 똑같은 데이터를 엑세스할 수도 있다.


   $pet.get('name') for only $pet.get('price')

이 방법은 똑같은 결과를 산출해 낸다. 그리고 실질적으로 무엇이 발생되었는지를 좀 더 확실하게 보여준다. 그렇지만 프로퍼티 노테이션을 사용하는 나머지 다른 한 방법이 좀 더 읽기 쉽고, 여러분의 템플릿과 데이터 클래스의 세부적인 구현과의 의존성을 줄일 수 있는 방법이다. 예를 들면, 여러분들은 public 메소드들인 getName()getPrice()를 가진 클래스로 List 안의 Map을 대체한다 하더라도 원래 예제 템플릿에서 쓰였던 아래 부분들은 계속해서 잘 동작하게 된다.


  $pet.name for only $pet.price

이것이 바로 Velocity가 여러분의 어플리케이션의 모델-컨트롤러 부분으로부터 view에 대한 커플링을 줄일 수 있도록 해 주는 한가지 방법이다. Velocity의 레퍼런스를 이용한 데이터 엑세스 지원은 강력하면서도 유연한 기능이다. 여러분들의 템플릿 레퍼런스는 퍼블릭하게 엑세스할 수 있는 메소드들에 대응하기 위해서 또는 Velocity의 프로퍼티 형식을 통해서 엑세스하는 것이 가능하게 하기 위해서 필요하다.

지시자(Directives)
지시자(Driective)는 VTL의 나머지 중요한 부분이다. 레퍼런스들처럼 그것들도 템플릿 내에서 다른 non-VTL 컨텐트와 자유롭게 혼용될 수 있다. VTL 내의 지시자(directive)는 다음과 같다.


    #set()
    #if()
    #else
    #elseif()
    #end
    #foreach()
    #include()
    #parse()
    #macro()

#set() 지시자는 템플릿 내에서 레퍼런스 값을 셋팅하게 해 준다. 이것은 컨텍스트 내에 기존에 존재하는 객체를 대체할 수 있거나 새로운 객체로 생성할 수 있다. 프리젠테이션 로직에 있어 디자이너에게 아주 유용한 점은 #set()는 정수에 대한 일반적인 수학적 연산(+,-,/*,%)을 허용할 뿐만 아니라 참/거짓도 평가할 수 있다는 것이다.


    #set( $startrow = 0)
    #set( $count = $current + 1 )
    #set( $isReady = ( $isOn && $isOpen) )

지시자의 #if() #else #elseif() #end 셋트는 많은 프로그래밍과 스크립트 언어에서와 마찬가지로 일반적인 if/then/else 기능을 제공해준다. #if()와 #elseif()는 Boolean 값으로 처리할 수 있는 논리적 연산자인 &&, ||, !, ==, >, >=, <, <=를 사용할 수 있는 표현식을 가지게 된다.


    #if( $value > 5 )
      bigger
    #elseif( $value < 5 )
      smaller
    #else
      just right
    #end

#foreach() 반복 지시자는 두개의 아규먼트를 갖는다. 각각의 값이 할당되는 변수에 대한 레퍼런스와 반복을 수행하게 될 컬렉션 값이 있다. 거기에 약간의 "문법적으로 설탕" 역할을 하는 in 단어가 포함된다.


    #foreach( $item in $itemList )
      My $item.
    #end

#foreach()가 지원하는 컬렉션의 타입으로는 다음과 같은 것들이 있다.

  • Object[]
  • java.util.Collection
  • java.util.Map (iterates over the mapped values)
  • java.util.Iterator
  • java.util.Enumeration

#include()#parse() 지시자는 둘 다 아규먼트로써 템플릿이나 스태틱 리소스를 가지고 템플릿이나 리소스를 적절한 장소에 포함해서 output stream으로 렌더링한다는 점에서 비슷하다. 차이점은 #include()는 어떠한 처리과정 없이 특정 리소스의 내용을 포함(include)하고 #parse()는 특정 리소스를 템플릿처럼 취급하여 현재 컨텍스트에 대하여 모든 지시자들과 레퍼런스들을 처리하게 된다.

여러분들은 이 두개의 지시자를 동적으로 결과물을 생성하는데 사용할 수 있다. 예를 들면, 웹 어플리케이션에서 여러분은 서로 다른 네이게이션 레이아웃이나 컬러/디자인 스키마를 가진 최상위 템플릿들과 같은 여러분의 페이지를 위한 골격을 세울 수 있다. 그런 다음에 동적/정적 컨텐트 엘리먼트들이 사용자와 어플리케이션 상태에 근거하여 런타임시에 #parse()나 (그리고) #include()된다.


    <table>
      <tr><td> #parse( $header ) </td></tr>
      <tr><td> #parse( $body ) </td></tr>
      <tr><td> #parse( $footer ) </td></tr>
    </table>

#macro() 지시자는 디자이너로 하여금 지시자 같이 호출될 수 있는 Velocimacro라고 불리는 파라미터화된 VTL code를 생성할 수 있도록 해 준다. 서브루틴과 비슷한 이러한 형태는 여러분들이 재사용할 수 있는 더 쉬운 사용, 가독성, 유지보수성을 위한 명시적 파라미터들을 가지고 있는 VTL 조각들을 생성할 수 있도록 해 준다. Velocimacro는 훌륭하면서도 강력한 툴이고, 아래에 나오는 예제는 최상의 예제이다.

Velocimacro 예제
여러분들이 디자이너가 <select> 양식을 만들기 위해 사용해야 하는 VTL을 캡슐화시키길 원한다고 가정을 해 보자. 여러분들은 regular VTL code 안에 Velocimacro를 정의할 수 있다.


  #macro( select $name $displayname $choicelist )
    <SELECT name=$name>
    <option value="">$displayname</option>
    #foreach( $choice in $choicelist )
      <option value=$choice>$choice</option>
    #end
    </select>
  #end

이렇게 되면, 여러분들이 HTML <select> 형식이 필요로 하는 어느때라도 여러분들은 단순히 지시자처럼 Velocimarco를 호출하기만 하면 된다.


  Please choose:  #select( "size" "--SIZE--" $sizelist )

렌더링 될 때, Velocity는 아규먼트들을 넘김으로써 Velocimacro를 자동적으로 확장한다.

Velocimacro는 recursion 능력, private local context를 지원, 그리고 심지어 private, template-specific namespace들로 분할 되어지는 능력과 같은 많은 흥미로운 형태를 가지고 있다. 더 많은 정보를 원한다면, Velocity 웹 사이트에 있는 사용자와 개발자 문서를 참조하길 바란다.

VTL 잡동사니
몇가지 다른 VTL 형태를 언급해야만 할 거 같다. VTL은 여러분들이 템플릿 내에서 integer, string 문자열, Boolean 값, 객체의 배열, 연속적인 integer의 배열들을 생성할 수 있도록 허용한다.


  #set( $myint = 5 )
  #set( $mystring = 'Hello There')
  #set( $mybool = true )
  #set( $objarr = [ "a", $myint, $mystring, false ] )
  #set( $intrangearr = [1..10] )

VTL은 또한 참조값(지시자와 Velocimacro들을 포함해서)들을 삽입할 수 있는 쌍따옴표(")를 사용해서 string 문자열을 가지게 만들수도 있다.


  #set( $foo = 'bar')
  #set( $interp = "The value is '$foo'")

$interp의 결과 값은 문자열 The value is 'bar'가 된다.

Velocity 웹 사이트에 완성된 VTL 문서를 찾을 수 있다.

똑같은 데이타, 다른 양식(템플릿)
이제 여러분들은 레퍼런스와 지시자들을 알았으니 우리의 애완동물가게 email 예제를 계속해 보자.

여러분들의 email 메시지를 HTML로 전환한다고 가정해 보자. Velocity를 이용하면 여러분들은 템플릿을 변경함으로써 손쉽게 전환하는 것을 할 수 있다. 여러분들의 코드는 변하지 않고 고스란히 남아있게 할 수 있다. 여기에 똑같은 데이터로부터 HTML을 생성해 내는 다른 템플릿이 있다.


  <HTML>
    <HEAD>
      <TITLE>Pet Store Sale!</TITLE>
    </HEAD>


    <BODY>
      <CENTER>
      <B>$petList.size() Pets on Sale!</B>

      <BR/>
      We are proud to offer these fine pets
      at these amazing prices.  
      <BR/>
      This month only, choose from:
      #set( $count = 1 )  
      <TABLE>
        #foreach( $pet in $petList )
          <TR>
            <TD>$count)</TD>
            <TD>$pet.name</TD>
            <TD>$pet.price</TD>
          </TR>
          #set( $count = $count + 1 )
        #end
      </TABLE>
      <BR/>

      <I>Call Today!</I>
      </CENTER>

    </BODY>
  </HTML>

이 템플릿을 사용하면, 여러분의 email body는 이제 아래처럼 보이게 된다.


  <HTML>
    <HEAD>
      <TITLE>Pet Store Sale!</TITLE>
    </HEAD>


    <BODY>
      <CENTER>
      <B>3 Pets on Sale!</B>

      <BR/>
      We are proud to offer these fine pets
      at these amazing prices.  
      <BR/>
      This month only, choose from:
      <TABLE>
          <TR>
            <TD>1)</TD>
            <TD>horse</TD>
            <TD>$100</TD>
          </TR>
          <TR>
            <TD>2)</TD>
            <TD>dog</TD>
            <TD>$59.99</TD>
          </TR>
          <TR>
            <TD>3)</TD>
            <TD>bear</TD>
            <TD>$3.99</TD>
          </TR>
      </TABLE>
      <BR/>

      <I>Call Today!</I>
      </CENTER>

    </BODY>
  </HTML>

이 예제는 어떻게 Velocity가 여러분들로 하여금 데이터와 코드로부터 프리젠테이션을 분리할 수 있도록 만들어 주는지를 잘 보여 주고 있다. 우리는 다른 템플릿을 가지고 똑같은 데이터와 코드를 사용했다.

XML template
마지막 애완동물 가게 예제로써 XML 문서를 생성해 내는 똑같은 프로그램을 이용하자. 다시 한번 우리는 템플릿을 변경한다.


<?xml version="1.0"?>

<salelist>
#foreach( $pet in $petList )
  <pet>
    <name>$pet.name</name>
    <price>$pet.price</price>
  </pet>
#end
</salelist>


똑같은 프로그램과 이 템플릿을 이용하여 아래의 결과를 얻을 수 있다.


<?xml version="1.0"?>

<salelist>
  <pet>
    <name>horse</name>
    <price>$100.00</price>
  </pet>
  <pet>
    <name>dog</name>
    <price>$59.99</price>
  </pet>
  <pet>
    <name>bear</name>
    <price>$3.99</price>
  </pet>
</salelist>

Velocity와 Servlet를 이용한 프로그래밍
마지막으로 서블릿과 함께 Velocity를 사용해 보자. Velocity 배포본은 org.apache.velocity.servlet.VelocityServlet라는 서블릿 기반 클래스를 포함하고 있다. 이 org.apache.velocity.servlet.VelocityServlet 클래스는 모든 Velocity와 Servlet API를 조절(handle)할 수 있게 함을로써 여러분들이 여러분들의 어플리케이션에만 신경을 쓸 수 있게 만들어준다. (대부분 아주 간단하다). 최소한 여러분들은 컨텍스트 안에 데이터를 집어 넣고, 템플릿을 리턴받는 메소드만은 구현해야만 한다. 아래의 클래스는 request를 핸들링하고 Velocity를 사용하여 템플릿을 렌더링한다(실제 에러핸들링 없이).


import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.servlet.VelocityServlet;

import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;


public class Sample extends VelocityServlet
{
    public Template handleRequest( HttpServletRequest request,
         HttpServletResponse response, Context context )
    {        
        /* organize our data  */

        ArrayList list = new ArrayList();
        Map map = new HashMap();

        map.put("name", "horse");
        map.put("price", "$100.00");
        list.add( map );

        map = new HashMap();
        map.put("name", "dog");
        map.put("price", "$59.99");
        list.add( map );

        map = new HashMap();
        map.put("name", "bear");
        map.put("price", "$3.99");
        list.add( map );

        /* add that list to the context  */

        context.put("petList", list);

        /* get the template */

        Template template = null;
      
        try
        {
            template =  getTemplate("petstoreemail.vm");
        }
        catch( Exception e )

        {
            System.out.println("Error " + e);
        }

        return template;
    }}

환경설정과 디플로이가 적절하게 되면, 이것은 HTML을 생성하는 애완동물 가게 예제와 같이 똑같은 결과를 생성해 내고 서블릿 엔진이 클라이언트 브라우져로 전달하게 될 것이다.

템플릿, deployment information과 추가적인 설정 소스 코드를 포함하여 완벽한 작업 예제를 원한다면 Velocity Project에 의해 제공되는 배포본 안의 서블릿 예제를 보길 바란다.

이제는 여러분들의 차례이다.
Velocity는 아래와 같은 특징을 가지고 있다.

  • 디자이너와 프로그래머 둘다 사용하기 쉽다.
  • 코드로부터 프리젠테이션 형식을 분리해 준다.
  • 기존에 존재하는 클래스를 사용할 수 있게 만들어준다.
  • 어떠한 자바 어플리케이션과 같이 작업할 수 있다.
  • 무료이고 business-friendly open source license인 Apache

    Velocity를 사용하는 것에 관하여 흥미로운 점들 중 한가지는 템플릿 모델의 간결함이 "올바른 방법"으로 수행할 수 있도록 만들어 줄 수 있다는 것이다.

    여러분들은 이 주장을 확인할 수 있다. 이제 여러분들은 Velocity를 가지고 시작하는 방법을 알았고, 진입장벽이 꽤나 낮다는것을 확신할 수 있었을 것이다. 예제들은 작성하기 쉽고 서블릿이나 standalone 프로그램 둘다 적용가능 했다. 여러분들은 Jakarta Velocity Project 웹 사이트로부터 배포본을 다운받음으로써 Velocity와 여러분 자신이 친숙해질 수 있다. 이것은 기본적인 형태나 많은 향상된 형태들을 다룰 수 있는 standalone 어플리케이션과 서블릿 예제들을 가지고 있다. 문서는 잘 정리되어 있고 supportive 커뮤니티는 여러분들을 충분히 도와 줄 수 있다. 메일링 리스트에 등록해서 자유를 느끼기 바란다. 여러분들은 지금 당장은 Velocity에 대한 효용성을 가지지 못할 수도 있다. 그러나 매일 반복되는 프로그래밍 작업에서 이것이 얼마나 자주 문제들을 해결해 줄 수 있는지 놀라게 될 것이다.


    저자에 대해
    Geir Magnusson Jr.는 software개발, 시스템 아키텍쳐, 실시간 데이터 전송에 전문적인 독립 컨설턴트이다. 그는 Velocity Project에 대한 공헌자로 노력하고 있으며, Jakarta Project의 관리 커뮤니티에서 봉사하고 있다. 그 역시 Aapache Software Foundation의 멤버이다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:17

출처 : http://www.jlab.net

 

Snippets There have been a significant number of small example code fragments posted to the newsgroup which provide insights into some aspect of SWT programming. This section is an attempt to capture these for future reference. Most of these have been written by one of the SWT committers, but in some cases they were created by modifying examples provided by other readers of the newsgroup. For simplicity, no attempt has been made to attribute these to a particular author. If you see something you wrote here, and want that to be noted, please email platform-swt-dev@eclipse.org and a by-line will be added.
You should also remember that examples from the newsgroup are often edited for brevity rather than completeness. They are intended to guide the reader towards the correct solution, rather than be finished products. NOTE: Snippets are tested against the HEAD stream and may reference new API or require bug fixes from there.
Note also that the best way to report an SWT bug is to write your own snippet showing the problem and paste it into the bug report. For a snippet template, see: "What is a snippet?" in the SWT FAQ.
If you are new to SWT and need help compiling and running snippets, click here for instructions.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:17

iBATIS SQL Maps 2.0에 대한 소개

 

번역 허태명

 

iBatis SQL Maps는 여러분의 자바 객체와 관계형 데이터베이스 사이에서 이동하는데 매우 간단하고 유연한 수단을 제공합니다. 단 한 줄의 JDBC 코드 없이 진정한 SQL의 완전한 능력을 사용하세요!

 

SQL Maps 프레임워크는 일반적으로 관계형 데이터베이스에 접근하기 위해 필요로 하는 자바 코드의 양을 현저하게 줄여주는데 도움을 줍니다. 이 프레임워크는 매우 간단한 형식의 XML을 사용하여 자바 Bean을 SQL 문장에 매핑합니다. 단순성은 다른 프레임워크와 ORM 툴들에 비해 SQL Maps의 가장 큰 장점입니다. SQL Maps를 사용하기 위해 여러분은 단지 자바 Bean과 XML, SQL에만 친숙하면 됩니다. 새로이 배워야 할 것은 거의 없습니다. 테이블을 조인하거나 복잡한 쿼리를 실행하기 위해 필요로 하는 복잡한 설계는 없습니다. SQL Maps를 사용함으로써 여러분은 진정한 SQL의 완전한 능력을 당장 사용할 수 있습니다. SQL Maps 프레임워크는 대부분의 데이터베이스를 어떤 객체 모델로도 매핑할 수 있고 레거시 설계, 심지어 그것이 나쁜 설계라도 매우 유연하게 대처할 수 있습니다. 이것은 특정한 데이터베이스 테이블의 생성이나 객체, 코드 생성없이 모두 이루어 집니다.

궁금한가요? 여기에 왜 여러분이 iBATIS SQL Maps를 좋아할 수 있는지 10가지 이유가 있습니다.

왜 iBATIS SQL Maps를 사용하나요?

10

JDBC 드라이버를 가지고 있는 어떤 데이터베이스에서도 사용할 수

있습니다.(플러그-인이 필요없습니다.)

9설정가능한 캐쉬 (의존성 포함)
8Local 과 Global 트랜잭션 지원과 관리(JTA)
7간단한 XML 매핑 문서 구조
6Map, Collection, List와 기본형의 래퍼(Integer, String 등)을 지원합니다.
5자바 Bean 스타일의 클래스를 지원합니다.(get/set 메소드)
4복잡한 객체 매핑을 지원합니다. (list와 복잡한 객체 모델 등의 생성)
3객체 모델은 결코 완벽하지 않습니다. (변경이 필요없습니다!)
2데이터베이스 디자인은 결코 완벽하지 않습니다 (변경이 필요없습니다!)
1여러분은 SQL을 이미 알고 있습니다. 왜 다른 것을 배우는데 시간을 낭비합니까?

무엇보다 가장 좋은 것은, 100% Open Source 프리웨어입니다!

예제는 어떤가요? 실제 코드에서 MappedStatement가 어떻게 사용되는지 확인해보세요.

<!-- 다음은 간단한 SQL Map의 예제입니다. 이 쿼리는 DB 컬럼의알리어스를 자동적으로 자바 Bean 프로퍼티에 매핑해주는 SQL Map프레임워크의 기능을 사용합니다. Address 클래스가 다음과 같은프로퍼티를 가지고 있다고 생각해 보세요: id (int), description (String),street (String), city (String), province (String), and postalCode (String).SQL 문장(표준 SQL 기능)에서 컬럼 알리어스를 사용하고 result-class어트리뷰트에서 Address 클래스를 명명함으로써, 컬럼의 값들은 자동적으로자바 Bean에 매핑됩니다! 이것은 SQL Map 프레임워크를 사용하여 SQL 쿼리에서 객체(자바 Bean)을얻는 단지 여러가지 방법 중의 하나입니다. --><select id="getAddress" parameterClass="int"                         resultClass="examples.domain.Address">
select ADR_ID as id,
ADR_DESCRIPTION as description,
ADR_STREET as street,
ADR_CITY as city,
ADR_PROVINCE as province,
ADR_POSTAL_CODE as postalCode
from ADDRESS
where ADR_ID = #value#</select>

어떻게 자바에서 이것을 실행하나요?

/* 다음 간단한 몇 줄의 코드는 위의 문장의 #value# 파라미터에 전달할 키로써 * integer 5를 사용하여 위의 매핑된 문장을 실행합니다.  */Integer pk = new Integer(5);Address address = (Address)sqlMap.queryForObject("getAddress", pk);
아래에 더 많은 예제가 있습니다.
<!-- SQL Maps은 단지 select 쿼리에서만 사용되지 않습니다, 여기에 어떻게새로운 Address 클래스가 insert되는지 보여주고 있습니다. --><insert id="insertAddress" parameterClass="examples.domain.Address">
insert into ADDRESS ( ADR_ID,
ADR_DESCRIPTION,
ADR_STREET,
ADR_CITY,
ADR_PROVINCE,
ADR_POSTAL_CODE) values ( #id#, #description#, #street#,
#city#,
#province#,
#postalCode#)
</insert>// 이 insert 문장을 실행하는 것은 select 쿼리와 마찬가지로 쉽습니다.// executeUpdate 메소드는 insert, update, delete를 위해 사용됩니다.Address address = new Address();address.setId(15);address.setDescription("Bob's Comic Book Store");address.setStreet ("16 Somestreet");...sqlMap.insert ("insertAddress", address);<!-- 여기에 하나 이상의 결과를 리턴하는 예제가 있습니다. --> <select id="getProductByCategory" parameterClass="string" resultClass="examples.domain.Product"> select PRD_ID as id, PRD_CATEGORY as category, PRD_DESCRIPTION as description, PRD_RETAIL as retail, PRD_QUANTITY as quantity from PRODUCT where PRD_CATEGORY = #value#</select>// "개" 카테고리의 제품 리스트를 위해 위의 문장을 실행하는// 것은 다음과 같이 간단합니다.String category = "dog";List productList = sqlMap.queryForList("getProductByCategory", category);// 결과가 너무 많은가요? 자바 Bean에 호환되고 JSP에서 쉽게// 사용할 수 있는, 완전히 탐색가능하고 lazy-loading 기능과// 페이징이 가능한 제품의 list 는 어떤가요?// 페이지당 10개면 괜찮을까요?PaginatedList productList = sqlMap.queryForList("getProductByCategory", category, 10);productList.nextPage();productList.previousPage();productList.isNextPageAvailable();// etc.// 동일한 쿼리에서 단지 Product 클래스의 ID와 Description의 Map만 필요하나요?Map descriptionMap = sqlMap.queryForMap ("getProductByCategory", category, "id", "description");<!-- 레거시 시스템의 복잡한 전용 쿼리? 예를 들어, 아마 여러분은 오라클을사용하여 복잡한 parent/child의 트리를 구할 때? --><select id="getEmployeesByManagerRecursively" parameterClass="int" resultClass="examples.domain.Employee"> SELECT emp_id as id, emp_number as employeeNumber, emp_first_name as firstName, emp_last_name as lastName, emp_dept_code as departmentCode FROM employee START WITH emp_id = #value# CONNECT BY PRIOR emp_manager_id = emp_id;</select>Integer managerId = new Integer(15);List employeeList = sqlMap.queryForList("getEmployeesByManagerRecursively", managerId);

더 많은 예제를 보고 싶으세요?

JPetStore 4를 살펴보세요. JPetStore4는 최신의 기능을 많이 사용하는 완전한 어플리케이션의 훌륭한 예입니다.
그리고 또한 포괄적인 완전한 문서의 Developer Guide 가 있습니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:16

 

 

출처 : http://network.hanbitbook.co.kr/view.php?bi_id=670

 

하이버네이트(Hibernate) 입문
hanbit (2003-11-27 13:49:52)

저자: 닉 휴데커(Nick Heudecker), 역 김진회

데이터베이스로부터 객체를 얻어오거나, 저장하는 퍼시스턴스 계층을 만들거나, 유지 보수하는 작업에 엔터프라이즈 애플리케이션 개발의 상당 부분이 소요된다. 만약 데이터베이스의 스키마가 바뀌는 경우에는 애플리케이션 나머지 부분도 크게 변경해야 한다. 하이버네이트(Hibernate)는 자바 애플리케이션에 적합하기 때문에 사용하기 쉽고, 강력한 객체 관련 퍼시스턴스(Object-Relational Persistence) 프레임워크를 제공한다. 따라서 퍼시스턴스 계층과 나머지 계층 간의 독립성을 유지하도록 해준다.

하이버네이트는 복합 타입은 물론, 컬렉션(Collection)과 객체(Object) 간의 관계에 대한 지원을 한다. 객체에 대한 퍼시스턴스 제공과 동시에, 효율적인 캐싱 계층과 JMX(Java Manage Extensions)를 지원하고, 데이터베이스로부터 객체를 얻어올 수 있는 퀴리 언어까지 제공한다. 사용자 정의 데이터 타입과 동적인 빈(Bean) 객체 역시 지원한다.

하이버네이트는 Lesser GNU Public License 하에 출시되고 있다. 따라서 상업용 애플리케이션은 물론이고 오픈 소스 애플리케이션에도 충분히 사용할 수 있다. 하이버네이트는 오라클, DB2, PostgreSQL, MySQL 등을 비롯한 수많은 데이터베이스를 지원한다. 또한, 하이버네이트를 확장하기 위한 지원 및 툴을 제공, 좀더 쉽게 사용할 수 있는 방법 등은 활발하게 운영되고 있는 사용자 커뮤니티를 통해 도움을 받을 수 있다.

본 기사가 작성된 시점인 2003.6.17 현재 Hibernate 2.0.1가 출시되었다.
(†역자 주: 2003.11.5부로 Hibernate 2.1 beta 6 released가 출시되었다.)

작동 방법(How Hibernate Works)

바이트 코드 프로세싱이나 코드 생성이 아니라, 하이버네이트는 클래스의 퍼시스턴스 프로퍼티를 얻어오기 위해서 런타임 리플렉션을 이용한다. 저장되는 객체는 퍼시스턴스 필드와 필드 간의 관계에 대한 정보를 제공하는 매핑(mapping) 문서에 정의가 된다. 매핑 문서는 애플리케이션이 동작하면 컴파일되고, 클래스에 필요한 정보를 가진 프레임워크를 제공한다. 뿐만 아니라 매핑 문서는 데이터베이스 스키마 또는 Stub 자바 소스 파일생성 등의 기능을 이용할 때 사용된다.

매핑 문서의 컴파일된 컬렉션으로부터 SessionFactory 객체가 생성된다. SessionFactorySession 인터페이스로 퍼시스턴스 클래스를 관리하기 위한 매커니즘을 제공한다. Session 클래스는 퍼시스턴스 데이터 저장과 애플리케이션 간의 인터페이스를 제공한다. Session 인터페이스는 JDBC 연결을 래핑(wrapping)하고 있다. 이 JDBC 연결은 사용자에 혹은, 하이버네이트로부터 관리될 수 있다. 그리고 오직 하나의 애플리케이션 쓰레드에 의해서만 사용되도록 관리된다.

매핑 문서(Mapping Documents)

아래 매핑 문서는 Team, Player라는 클래스에 관한 내용을 담고 있다.
<hibernate-mapping><class name="example.Team" table="teams">  <id name="id" column="team id" type="long" unsaved-value="null">    <generator class="hilo"/>  </id>  <progerty name="name" culumn="team_name" type="string"length="15" not-mull="true"/>  <progerty name="city" culumn="city" type="string"length="15" not-mull="true"/>  <set name="players" cascade="all" inverse="true" lazy="true">    <key column="team id"/>    <one-to-many class="example.Player"/>  </set></class></hibernate-mapping>[SourceCode 1] example.Team 매핑 문서
<hibernate-mapping><class name="example.Player" table="players">  <id name="id" column="player id" type="long" unsaved-value="null">    <generator class="hilo"/>  </id>  <property name="firstName" column="first_name" type="string"length="12" not-null="true"/>  <property name="lastName" column="last_name" type="string"length="15" not-null="true"/>  <property name="draftDate" column="draft date" type="date"/>  <property name="annualSalary" column="salary" type="float"/>  <property name="jerseyNumber" column="jersey_number" type="integer" length="2" not-null="true"/>  <many-to-one name="team" class="example.Team" column="team_id"/></class></hibernate-mapping>[SourceCode 2] example.Player 매핑 문서
상당히 이해하기 쉬운 편이지만 설명이 필요한 부분도 있다. id 요소는 퍼시스턴스 클래스에 의해서 사용되는 주요키를 뜻하는 것으로 id 요소의 속성은 다음과 같다.
  • name: 퍼시스턴스 클래스에 의해서 사용되는 프로퍼티(† 역자 주: 자바 클래스에 정의된 프로퍼티의 이름)
  • column: 주요 키의 컬럼명
  • type: 자바 데이터 타입
  • unsaved-value: 이 값은 클래스가 데이터베이스에 저장이 되었는지 알기 위해서 사용된다. 예를 들어 id 속성이 널(null)이라면 하이버네이트는 이 객체가 저장이 안된 것으로 인식한다. 이 속성은 나중에 saveOrUpdate()라는 메소드를 호출할 때 중요하며, 자세한 설명은 나중에 하겠다.
generator는 주요 키를 생성하는데 사용되는 메소드를 명시한다. 여기서는 hilo generator가 사용되었지만, 하이버네이트는 10여 가지 key generation 메소드를 제공한다. 그뿐만 아니라, Composite Primary Key를 포함하여 개발자 자신만의 매커니즘을 이용하는 것도 가능하다.

property는 표준 자바 속성을 정의하고, 스키마에서 이것들이 컬럼으로 매핑되는 방법을 정의하게 된다. 속성은 컬럼의 길이, SQL 타입, 널 값 사용 가능 유무를 명시할 수 있다. property에는 column이라는 child element를 제공한다. 이 element는 컬럼에 대한 인덱스 명이나 특정 컬럼 타입 같은 추가적인 특징을 명시하기 위해 사용된다. br>
Team 클래스는 다음과 같은 element 블록을 가지고 있다. 이것은 Team에 속한 Players의 컬렉션을 나타낸다.
<set name="player" cascade="all" inverse="true" lazy="true">	<key column="team_id"/>	<one-to-many class="example.Player"/></set>[SourceCode 3] example.Team 컬렉션 정의
[SourceCode 3]은 Player 클래스에 정의된 양방향 매핑을 사용하여 PlayersTeam에 매핑된 것을 정의한다. key element로는 foreign key가 정의가 된다. one-to-many element는 정의된 집합 안에 어떤 객체가 담겨 있는지 정의한다. 예를 들어, Players라는 컬렉션은 Teameam_id라는 컬럼을 foreign key로 참조하며, 컬렉션 안에는 example.Player 객체를 담게 된다.

set element에 있는 lazyinverse라는 속성에 주목해야 한다. 컬렉션을 lazy="true"라 표기하는 것은 컬렉션을 포함하는 객체를 데이터베이스로부터 얻어올 때, 컬렉션이 자동으로 생성되지 않는다는 것을 의미한다. 예를 들어, 데이터베이스로부터 Team 객체를 얻어온다면 Players 객체는 애플리케이션이 접근할 때까지 생성하지 않을 것이다.

이렇듯, inverse element는 양방향성을 가진다는 것을 의미한다.

하이버네이트 프로퍼티(Hibernate Properties)

하이버네이트가 데이터베이스와 접속하거나 스키마를 생성하기 위해서 사용하는 프로퍼티는 hibernate.properties에 작성된다. (†역자 주: 원문에서는 Postgresql 데이터베이스에 접속하는 예제를 보여줬지만 본 기사에서는 역자가 테스트한 환경을 제시하겠다.)
hibernate.connection.username=webfx (데이터베이스에 접속할 수 있는 계정)hibernate.connection.password=webfx (데이터베이스에 접속하기 위해서 필요한 패스워드)hibernate.connection.url=jdbc:oracle:thin:@localhost:1521:SHINHWAhibernate.connection.driver_class=oracle.jdbc.driver.OracleDriverhibernate.dialect=net.sf.hibernate.dialect.Oracle9Dialect(사용하고자 하는 데이터베이스 종류에 따라서 하이버네이트가 제공한다.여기서는 Oracle 9i를 사용했으므로, Oracle9Dialect를 사용했다.)[SourceCode 4]
위의 4가지 속성은 JDBC 연결에서 많이 접해봤을 것이라 생각하므로 자세한 설명은 생략하겠다. hibernate.dialect 속성은 하이버네이트에서 제공하는 Hibernate Query Language(HQL)을 SQL로 변환하기 위해 필요한 SQL dialect를 지정한다. 이 속성 덕분에 애플리케이션의 Data Access Layer는 특정 데이터베이스에 종속되지 않게 할 수 있다.

SessionFactory 생성하기

SessionFactory는 팩토리가 생성될 때 지정한 컴파일된 매핑문서을 저장하고 있다. SessionFactory의 구성 변경 작업은 아주 직관적이다. 모든 매핑 정보는 net.sf.hibernate.cfg.Configuration의 인스턴스에 추가된다. 이것은 나중에 SessionFactory 객체를 생성할 때 사용하게 된다.
Configuration configuration = new Configuration().configure();Configuration configuration = new Configuration().configure("매핑 파일의 위치");[SourceCode 5]
위와 같이 매핑 정보를 가지고 있는 Configuration을 생성하는 방법에는 두 가지가 있다. 첫번째 방식처럼 매핑 파일의 위치를 지정하지 않으면 기본적으로 지정된 클래스 패스를 찾게 된다(이 경우는 개인적으로 추천하지 않는다). 외부에서 제공되는 모듈을 사용하는 경우, 각 모듈마다 설정 파일을 가지고 있을 경우가 있다. 이런 경우에는 설정파일을 한 곳에 모아놓고 관리하는 것이 유지 보수하기도 편리할 것이므로, 매핑 파일의 위치를 지정할 수 있는 두 번째 방식을 추천한다.
SessionFactory factory = congiruation.buildSessionFactory();[SourceCode 6]
Configuration 클래스는 SessionFactory의 생성시에만 필요하다. 그리고 팩토리가 생성된 후에는 버려진다. Session 인스턴스는 SessionFactory.openSession()을 호출함으로써 얻어지며 Session 인스턴스의 논리적인 lifecycle은 데이터베이스 트랜잭션의 lifecycle과 일치한다.

SessionFactory는 또한 XML 매핑 파일을 이용하여 설정할 수 있다. 매핑 파일은 지정한 클래스 패스의 루트 안에 위치시키면 자동적으로 인식되며, 직접 지정할 수도 있다. (개인적으로는 직접 지정하는 방식을 추천하지만, 패스를 직접 하드 코딩하는 얘기는 아니다. 구체적인 방법은 여기서는 다루지 않겠다.)

※주의사항: 하이버네이트의 프로퍼티를 설정하는 방법은 Properties 파일을 이용하는 방법과 XML 파일을 이용하는 방법이 있다. 이때 둘 중, 하나만 이용하도록 하자. 둘 다 지정된 클래스 패스에 있는 경우, 에러가 발생할 위험이 있다.

Persistent 클래스 생성 및 업데이트

하이버네이트를 이용하는 경우, 객체는 Transient 혹은 Persistent와 같이 두 가지 부류로 나누어진다. Transient 객체는 데이터베이스에 저장되지 않은 객체를 말하는 것으로 Transient 객체를 Persistent하게 만들기 위해서는 Session 클래스를 이용해서 데이터베이스에 저장해야 한다.
Player player = new Player(); // ... player 객체에 대한 처리 -> 이때 까지는 player 객체는 Transient 한 상태이다.Session session = SessionFactory.openSession();Session.saveOrUpdate(play);-> 비로서 player 객체는 Persistent하게 된다.[SourceCode 7]
saveOrUpdate라는 메소드는 id 프로퍼티가 null인 경우에는 객체를 저장(데이터베이스에 SQL INSERT)하고, null이 아닌 경우에는 업데이트 작업을 실행(데이터베이스에 SQL UPDATE)하게 된다. 위에서 언급된 unsaved-value 속성을 기억하는가? 이와 관련된 내용이다.

할당된 Players를 포함하고 있는 Team 객체를 생성하고 저장하고 싶다면, 다음과 같이 작업하면 된다.
Team team = new Team();team.setCity ("Detroit");team.setName ("Pistons");// add a player to the team.Player player = new Player();player.setFirstName ("Chauncey");player.setLastName ("Billups");player.setJerseyNumber (1);player.setAnnualSalary (4000000f);Set players = new HashSet();players.add(player);team.setPlayers(players);// open a session and save the teamSession session = SessionFactory.openSession();Session.saveOrUpdate(team);[SourceCode 8] Persisting 객체
Team 객체와 Set 컬렉션에 저장된 Player 객체가 저장된다.

Persistent 클래스 검색하기

데이터베이스로부터 얻어오고자 하는 객체의 주요 키 값을 알고 있다면, 아래와 같이 Session.load()를 이용하여 그 객체를 얻어올 수 있다.
// method 1: loading a persistent instanceSession session = SessionFactory.createSession();Player player = session.load(Player.class, playerId);// method 2: loading the Player's statePlayer player = new Player();Session.load(player, playerId);[SourceCode 9] Loading Persistent 인스턴스
주요 키 값을 알지 못한 상태에서 Persistent 객체를 얻어오기 위해서는 Session.find() 메소드를 사용해야 한다. find() 메소드는 HQL(Hibernate Query Language) 구문을 이용해서 java.util.List 형으로 객체들을 얻어올 수 있도록 한다. HQL에 대한 내용은 참고문헌을 찾아보도록 하자.

†역자 주: 혹시, HQL을 이용해야 한다고 듣자마자 "뭐야? SQL을 여기서도 쓰잖아?"라고 대꾸하는 사람이 있을 것으로 생각한다. 역자도 처음에는 그렇게 생각했다. 그렇지만 HQL은 하이버네이트 내에서 표준으로 쓰이는 쿼리 언어이므로 데이터베이스와 독립적으로 쓰일 수 있다. 그리고 상당히 객체 중심이므로 익히기도 편하다.

Persistent 클래스 삭제하기

Persistent 객체를 Transient 객체로 만드는 것은 Session.delete() 메소드를 이용하면 된다. 이 메소드는 특정 객체에 대해서 직접 delete() 메소드를 호출하거나, HQL을 이용하여 특정 조건에 만족하는 객체를 삭제할 수 있다.
// method 1 - deleting the Player loaded in SourceCode 8session.delete(player);// method 2 - deleting all of the Players with a//            salary greater than 4 millionsession.delete("from player in class example.Player where player.annualSalary> 4000000");[SourceCode 10] Persistent 객체 삭제
여기서 데이터베이스에서는 객체가 삭제되지만, 애플리케이션 상에서는 아직 참조하고 있다는 사실에 주목해야 한다. 객체의 컬렉션을 가지고 있는 객체를 삭제할 경우에는 매핑 파일에 cascade="delete"라고 지정해야 컬렉션에 있는 자식 객체까지 모두 삭제가 된다.

Persistence 클래스 대체안 마무리하면서

본 기사에서는 하이버네이트를 아주 간단하게 소개만 했다. 하지만 하이버네이트는 높은 성능을 제공할 뿐만 아니라 다른 오픈 소스나 상용 제품들과 비교해보았을 때에도 결코 뒤지지 않은 오픈 소스 퍼시스턴스 프레임워크를 제공한다. 하이버네이트에 대해 더 자세한 내용을 알고 싶다면 우선 http://www.hibernate.org를 방문하길 바란다.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:16
오늘은 전에 말씀 드렸던 이지 스트러츠 플러긴에 대해서 설명을 드릴까 합니다...
 
아직 스트러츠가 설치 되어 있지 않다면 http://struts.apache.org/에서 다운
받아 적당한 위치에 압축을 풉니다....
 
그리고 이지스트러츠는
http://easystruts.sourceforge.net/로 가서
이클립스의 Update Manager를 이용하여 설치하거나 직접 다운 받아 압축을 풀고 이클립스의
plugins 폴더로 복사하고 이클립스를 Reload 합니다..
이클립스의 메뉴중 Window에서 Preferences를 연후 Easy Struts를 선택하여 
이지스트러츠의 환경을 설정합니다..


사용할 스트러츠 버젼(이젠 거의 1.1을 쓰겠죠..)을 선택하고  Add JARs.. 버튼을 누르고
먼저 설치해 놓은 스트러츠의 lib 폴더에서 모든 jar파일을 선택하여 추가합니다...
한번에 안되니까 하나씩 하나씩 추가합니다...
 

그리고 바로 아래에 있는 Add TLDs... 버튼을 누르고 위에서와 같은 폴더에서 같은 방법으로
모든 tld파일을 추가합니다...
jar 파일들과 tld 파일들을 모두 추가했으면 Ok 버튼을 눌러 이지스트러츠 설정을 모두 마칩니다..
이제 본격적으로 이지스트러츠를 이용하여 웹 어플리케이션을 만들어 보겠습니다..
톰캣이나 레진과 같은 이클립스 플러그인이 설치 되어 있지 않다는 가정하에 설명을 드릴까 합니다.
자바프로젝트를 하나 만듭니다...
저는 편의상 그냥 이클립스 ?p스페이스에 만들었습니다..
자바프로젝트로 만들었지만 웹프로젝트처럼 사용하기 위해 소스폴더와 클래스파일 폴더를
아래와 같이 생성해 줍니다.
이제 만들어진 strutsTest라는 프로젝트에 이지스트러츠를 이용해 스트러츠를 적용합니다..
먼저 선택한 프로젝트에서 New > Other..를 선택합니다..
자바를 확장하면 Easy Struts가 나옵니다..
여기서 먼저 Add Easy Struts support를 사용하여
앞서 Preferences >  Easy Struts에서 설정해 놓은 환경을 적용합니다...
 
Webapp folders는 root로 사용하려면 /만 쓰면 됩니다..
그리고 기본 패키지와 다국어 처리를 위한 resource파일을 적당한 패키지와 이름으로 설정합니다..

앞에서 설정한 jar파일들과 tld파일들이 해당 프로젝트에 복사되어 스트러츠를 사용할 수 있는
환경으로 변합니다.
 
이제는 폼을 만들어 봅니다.
 
Form name을 입력하고 실제로 자바파일로  생성될 Form type도 적절히 입력합니다.
그리고 Form properties도 필요한 만큼 JSP input type을 설정해 가면 입력합니다...



 
입력한 Form properties를 바탕으로 만들어질 (Create JSP) input 명을 입력합니다..

 
Finish버튼을 누르면 아래처럼 폼파일과 login.jsp파일이 생성됩니다.

 
그런데 폼파일에 에러표시가 있습니다...
이것은 웹프로젝트가 아니라 서블릿 API가 없어서입니다..
 
각기 사용하는 서블릿엔진이 설치되어 있는  디렉토리의 lib파일에 있는 서블릿 jar파일을
클래스패스 설정하면 해결됩니다....
 
저는 설정이 간편한 resin 서버를 사용하였기에 아래와 같이 jsdk23.jar파일을 연결했습니다..
strutsTest 프로젝트 선택 > 마우스 오른쪽 메뉴 > Properties > Java Build Path > Libraries
> Add External JARs로 연결하면 됩니다...

 
이제는 Action을 설정해 봅니다..
그전에 Action설정시 사용할 success.jsp를 login.jsp가 있는 곳에 하나 만듭니다..
간단히 login이 성공되었다는 메제지를 뿌리도록 만들어 보았습니다..
------ success.jsp ---------
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<html>
 <head>
    <title>Success</title>
 </head>
 <body>
  Hello, <bean:write name="id"/>  Welcome Easy Strust!!!
 <body>
</html>
 

Path를 설정하고 Type도 적절히 입력합니다..
Action에 연결한 Form name을 Browse...버튼을 눌러 선택합니다..


(Create JSP) input 의 Browse...버튼을 눌러 login.jsp를 선택합니다...

Next...버튼을 눌러 다음으로 진행합니다...

 
Forward name을 적절히 입력하고 실제적으로 Forwarding될 jsp를 이전에 만들어 놓은 success.jsp로 연결합니다...



이제 다 되었습니다...

 
TestAction.java를 테스트를 위해 아래처럼 변경합니다...
String id = TestForm.getId(); //폼에 저장되어 있는 id
  
 request.setAttribute("id", id); // success.jsp에서 사용할 id 저장.
 return mapping.findForward("success");  //success.jsp로 forward
 
 --------------------------- TestAction.java ----------------------------
// Created by Xslt generator for Eclipse.
// XSL :  not found (java.io.FileNotFoundException:  (Bad file descriptor))
// Default XSL used : easystruts.jar$org.easystruts.xslgen.JavaClass.xsl
package test.easystruts.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import test.easystruts.form.TestForm;
/**
 * TestAction.java created by EasyStruts - XsltGen.
 * http://easystruts.sf.net
 * created on 07-07-2004
 *
 * XDoclet definition:
 * @struts:action path="/Test" name="TestForm" input="/login.jsp" validate="true"
 * @struts:action-forward name="/success.jsp" path="/success.jsp"
 */
public class TestAction extends Action {
 // --------------------------------------------------------- Instance Variables
 // --------------------------------------------------------- Methods
 /**
  * Method execute
  * @param ActionMapping mapping
  * @param ActionForm form
  * @param HttpServletRequest request
  * @param HttpServletResponse response
  * @return ActionForward
  * @throws Exception
  */
 public ActionForward execute(
  ActionMapping mapping,
  ActionForm form,
  HttpServletRequest request,
  HttpServletResponse response)
  throws Exception {
  TestForm TestForm = (TestForm) form;
  String id = TestForm.getId();
  
  request.setAttribute("id", id);
  return mapping.findForward("success"); 
  //throw new UnsupportedOperationException("Generated method 'execute(...)' not implemented.");
 }
}
-----------------------------------------------------------------------------
 
에러를 막기위해 TestForm.java에서 validate를 일단 막습니다.
validate메소드를 만들지 않으려면 폼을 생성할때 validate메소드에 체크표시를 없애면 만들지
않습니다..reset 메소드도 마찬가지입니다..
 
보통 개발시에는 validate와 reset을 사용하지 않습니다.
클라이언트에서 자바스크립트를 이용합니다..
 
public ActionErrors validate(
  ActionMapping mapping,
  HttpServletRequest request) {
  return null;
  //throw new UnsupportedOperationException("Generated method 'validate(...)' not implemented.");
 }
 
 
login.jsp를 열고
<html:form action="/[ACTION_PATH]"> 이부분을
<html:form action="/Test"> 과 같이 변경합니다.
"/Test" 는 Easy Action을 만들때 지정한 Path명입니다..
 
struts-config.xml을 열어서도 위에서 만들었던 폼과 액션 기타 작업을 할 수 도 있습니다.
소스 탭을 열어서 직접 수정하거나 카피앤 페이스트하여 변경도 할 수 있습니다.
이때 struts-config.xml에 에러가 나는데 이때는 struts-config.xml 파일을 리로드 시켜야
에러가 사라집니다..
이제 웹서버를 띄워서 확인해 봅니다...
저는 웹어플리케이션명을 /strutsTest로 해서 만들어 보았습니다.
레진은 아래와 같이 resion.conf파일에 추가하면 됩니다..
<web-app id='/strutsTest' app-dir='D:eclipse2workspacestrutsTest'/>






 
/login.jsp도 Action명으로 대체 할 수있습니다..
위에서 생성했던 Action을 응용하여 한번 해보세요...
-------------------- LoginAction.java ---------------------------------------
// Created by Xslt generator for Eclipse.
// XSL :  not found (java.io.FileNotFoundException:  (Bad file descriptor))
// Default XSL used : easystruts.jar$org.easystruts.xslgen.JavaClass.xsl
package test.easystruts.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
/**
 * LoginAction.java created by EasyStruts - XsltGen.
 * http://easystruts.sf.net
 * created on 07-07-2004
 *
 * XDoclet definition:
 * @struts:action
 */
public class LoginAction extends Action {
 // --------------------------------------------------------- Instance Variables
 // --------------------------------------------------------- Methods
 /**
  * Method execute
  * @param ActionMapping mapping
  * @param ActionForm form
  * @param HttpServletRequest request
  * @param HttpServletResponse response
  * @return ActionForward
  * @throws Exception
  */
 public ActionForward execute(
  ActionMapping mapping,
  ActionForm form,
  HttpServletRequest request,
  HttpServletResponse response)
  throws Exception {
  return mapping.findForward("login");
  //throw new UnsupportedOperationException("Generated method 'execute(...)' not implemented.");
 }
}
 
struts-config.xml도 아래처럼 추가되었겠죠...
<action path="/Login" type="test.easystruts.action.LoginAction" validate="false">
     <forward name="login" path="/login.jsp" />
</action>

 
위와 같이 했으면
http://localhost:8080/strutsTest/login.jsp 로 접근하지 말고
http://localhost:8080/strutsTest/Login.do 하면 같은 결과를 볼 수 있습니다..
 
위와 같이 하면 <forward name="login" path="/login.jsp" />의 path의 jsp이름만
변경하면 항상 Login.do로 접근할 수 있는 장점이 있습니다..
 
잘되었나요..수고하셨습니다....
 
데이타소스 기능은 기다지 많이 사용하지 않습니다..
자체 웹서버가 지원해 주는 데이타소스를 공통 static메소드로 구현해 많이 사용합니다..
 
이상으로 가장 많이 사용하는 기능위주로 설명을 드렸습니다.. 
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:15
자바 기술 변화를 한눈에 보여주는 그림

e-비즈니스의 실현을 위한 J2EE 기술이 등장한지도 벌써 5년이란 세월이 흘렀습니다.
그 동안 자바 개발자들은 다양한 프로젝트 경험을 통해 스스로의 실력을 조금씩 발전시켜 왔지요..
최근들어 자바 기술은 또 한번의 도약을 준비하고 있는 듯합니다.
웹 서핑을 하던 중 우연히 한장의 그림을 발견하게 되었습니다.
자바월드였던가? 안타깝게도 정확한 출처가 기억나지 않네요.. Rolling Eyes Rolling Eyes Rolling Eyes

휘갈겨 쓴 그 그림은 최근의 자바 기술이 어떻게 변화하고 있는지를 잘 정리하고 있었습니다.
(이 글 마지막의 원본 그림을 참고하세요..)
그 그림을 이용해서 제가 생각하는 변화의 흐름을 한번 이야기해볼 욕심으로 몇 자 끄적거립니다.

Exclamation [변화는 이미 시작되었고..]


위의 그림은 제가 발견한 한 장의 그림을 PPT로 다시 정리해본 것입니다.
MVC 패턴에 기반한 n-계층 C/S 환경을 기준으로 얘기를 풀어보겠습니다.


(1) 표현 계층 (Presentation Layer)

그림에서도 알 수 있듯이 최근 가장 급격한 변화를 겪고 있는 곳이 표현 계층의 기술입니다.
모델 2 방식이나 태그라이브러리를 활용한 뷰와 로직의 분리는 이미 당연한 것처럼 사용되고 있지요.
벨로시티(Velocity)와 타일즈(Tiles) 태그 라이브러리가 잠시 주목을 받았지만,
최근에는 더 큰 변화의 물결에 휩쓸려 말이 쏙~ 들어가 버렸습니다..
(사실 CompositeView 패턴을 활용해서 각각의 화면을 분할 정의하고,
ScreenDefinition.xml에 그 정보를 기록한 다음 ScreenManager로 통합하는
기법에서 타일즈와 벨로시티는 그 가치가 아주 크다고 생각됩니다.
하지만, 개발생산성 측면에서 분명한 약점이 존재한다는 사실을 인정하지 않을 수 없네요..)

요즘들어 표현 계층에서 가장 많은 관심을 받는 것은 당연 JSP 2.0과 JSF일 것입니다.
JSP 2.0도 메이저 버전이 올라간 많큼 확실히 많은 기능이 변화했습니다만,
많은 분들이 JSF에 큰 관심을 갖고 계시더군요..
Sun에서 발표한 Java Studio Creator와 IBM의 WebSphere Studio 5.x가
JSF를 지원하는 대표적 IDE라고 할 수 있는데요,
뷰 개발에 있어 닷넷의 웹폼 및 Visual Studio에 버금가는 생산성을 기대할 수 있을 것 같습니다.
오픈소스로 개발되는 JSF 컴포넌트들인 MyFaces도 관심을 가질만 하구요..
MyFaces에 포함된 캘린더 컴포넌트는 이번에 꼭 한번 써볼 생각입니다. ^^*

Exclamation 잠깐! JSF 메뉴얼 있어요~ ^^*
http://www.java-inside.co.kr 에 가시면 김승규님이 작성한 JSF 메뉴얼이 있답니다.
훌륭하게 작성된 메뉴얼인데 의외로 알려지지 않았더군요.. 거참.


이번 달(9월호) 마소지를 받아보니 X 인터넷 기술의 하나인 플렉스(FLEX) 자료가 별지로 들어있더군요..
이전에 짧은 소개글을 하나 썼던 기억이 납니다. 관심있으신 분은 읽어보세요.. ^^*

Exclamation X-인터넷에 대한 짧은 소개
http://java.techedu.net/bbs/view.php?id=mdahotissue&no=3



(2) 제어 계층 (Control Layer)

제어 계층의 주된 역할은 분리된 뷰와 로직을 어플리케이션 구성에 따라 적절히 연결해주는 것이라고 할 수 있겠지요?
물론 로그를 남기거나, 접근 제어 등을 제어 계층에서 할 수 도 있겠지만 말이죠.
제어 계층을 구현하는데 있어서 가장 핵심이 되는 설계 기법은 Service to Worker 패턴이 아닐까 싶습니다.

Exclamation Service to Worker 패턴 = Front Controller 패턴 + Command 패턴 + Service to Locator 패턴 + View Helper 패턴


전체 시스템의 설정을 XML로 관리하고, 사용자의 모든 요청은 Servlet으로 구현된 Front Controller에게 전달되죠.
Front Controller는 XML 설정을 참고해서 적당한 Command를 실행함으로써 동일한 방법으로 요청을 처리할 수 있구,
그 결과는 View Helper를 이용해서 프리젠테이션 계층에 전달되는 형태입니다.

제어 계층은 현재 Struts가 대세를 이루는 가운데 WebWork와 경쟁하고 있는 것처럼 보여집니다.
초기에는 수 십개의 오픈소스 WAF(Web Application Framework)들이 경쟁했지만,
2개의 프레임워크가 이미 안정화된데다 널리 알려져 있기 때문에, 당분간 별다른 변화는 없을 것 같으네요.


(3) 어플리케이션 로직 계층 (Application Logic Layer)

어플리케이션 로직 계층에서 뜨거운 감자는 EJB로 보여집니다.
많은 사람들이 프로젝트에서 EJB를 사용하고 있지만, 그만큼 EJB에 대한 불만도 커져 있는게 사실이지요..
EJB 3.0이 얼마나 개발자들의 불만을 해소시켜 줄지는 아직 의문입니다.

그런 의미에서 EJB의 사용 여부와 관계없이 WAS가 제공해주는 다양한 기능들을 효과적으로 지원하는,
Pico Container나 Spring 같은 IoC 개념이 적용된 경량급 컨테이너에 대한 관심이 높아져가는 듯 합니다.
"Writing Enterprise Application without EJB"가.. 주목을 받는다고 할까요?

현재로써는 세션빈과 BMP 방식의 엔티티빈 사용이 주류를 이루고 있다는 하지만,
일반 자바 객체(POJO)를 IoC에서 이용하는 방법 + ORM 기법과
EJB 3.0 중 누가 최후의 승자가 될지는 조금 더 지켜봐야 할 듯 합니다.
큰 변화를 앞두고 가장 긴장감이 흘러 넘치는 계층인 듯 .. ^^*


(4) 데이터 처리 계층 (Persistence Layer)

데이터 처리 계층에서는 DB를 이용하는 경우만 잠깐 얘기해볼께요.
사실 개발자 개인이 잘 만들어 놓은 DAO 객체가 엔티티빈에 비해 기능적으로나 성능적으로 뒤쳐질 이유는 하나도 없다고 봅니다.
WAS가 제공해주는 트랜잭션 처리와 자원 관리의 이점을 배제한다면 말이죠.
EJB 2.0부터 CRM을 통한 향상된 CMP 방식이나 로컬 인터페이스를 통한 성능 향상,
EJB QL을 통한 DBMS 비의존적 쿼리 작성 등이 가능해졌지만,
제 경험으로는 여전히 대규모 어플리케이션 개발에서 많은 점이 부족했습니다.
Floyd가 소개한 다양한 EJB 패턴들을 적용해서 그 부족한 점을 꾸역꾸역 메꾸긴 했지만,
가끔씩 입에서 욕이 튀어나오는 건 어쩔 수 없더군요..

DB 정보를 읽어들여 JBuilder 등의 IDE에서 자동으로 CMP 방식의 엔티티빈을 만들고,
마우스 조작 만으로 관계 설정을 하고 EJB-QL로 필요한 쿼리를 입력하는 방식은..
편하고 높은 생산성을 보장해주긴 했지만,
조금만 문제가 복잡해져도 결국 BMP 방식으로 코드를 작성할 수 밖에 없게끔 강요했습니다.

그렇다고 WAS의 막강한 지원을 포기할 형편도 아니지요.
커넥션 풀이야 자카르타 프로젝트의 오픈소스를 빌려 쓴다 하더라도,
트랜잭션 처리를 일일이 JTA로 하드코딩할 수 없으니까요.. 보안도 맘에 걸리구.

하지만 객체 관계 매핑(ORM) 솔루션이 많이 소개되면서 오히려 엔티티빈보다 더 나은 작업 환경이 갖춰진 듯합니다.
그 선두 주자가 Hibernate인 점은 좀 의아하지만 말이죠..
ORM의 등장은 우리를 좀 더 EJB에 대한 강압으로부터 자유롭게 해 줄 것 같습니다. ^^*


(5) 공통-통합 (Common/Cross-Writing)

모든 계층을 아우르는 일반 영역에서 관심을 끌고 있는 것들이 어디 한두가지 겠습니까만은,
그 중에서도 AOP와 XML 기반의 웹 서비스에 대한 논의가 가장 활발한 듯 합니다.

AOP(Aspect Oriented Programming)의 개념은 어쩌면 MDA와 결합해서 개발자들의 오랜 습관을 뒤흔들지도 모르겠네요..
미리미리 공부해둬야 겠습니다. ^^*


주의!!
여기에 적힌 글들은 정확한 산업 통계나 공신력있는 기관의 정보가 아닙니다.
그저 물개선생이 우연히 발견한 한장의 그림을 통해,
개인적으로 생각하고 있는 최근의 기술 변화 흐름을 짤막하게 적어본 것입니다.
아주 공감이 가는 그림인데, 다른 분들도 공감하실까.. 싶어서요.. ^^*
너무 심각하게 읽지 말아주세요.. Twisted Evil Twisted Evil Twisted Evil Twisted Evil
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:15

제3회 Ant(으)로EJB개발을 효율화


오카모토 타카시
NTT데이터 기술개발 본부/Ja-Jakarta Project
2003/2/19


 본고에서는 , 전회 「Ant으로Java의 빌드를 간단하게 한다」 것Ant의 사용법의 응용편이라고 하는 것으로 ,Ant의 태스크로서 이용할 수 있는 코드 생성 엔진XDoclet을 이용해EJB를 간단하게 생성하는 방법에 대해 해설합니다. XDoclet(을)를 이용하면(자) ,1개의 것JavaBean을 작성하는 것만으로 ,EJB에 필요한 리모트/홈 인터페이스 , 각종 배치 기술자(이하DD) 를 자동적으로 생성할 수 있게 됩니다. XDoclet의 이용은 ,EJB개발의 효율화 , 관리의 수고의 삭감에 매우 효과가 있습니다. EJB의 개발의 번잡함이 곤란하고 있는 사람 ,EJB을 사용해 보고 싶지만 , 만드는 방법이 어려워서 사용할 수 없었던 사람은 , 꼭 본고를 읽어 실천해 보세요.

 번잡한EJB개발을 편하게 하고 싶다

 여러분은 ,EJB을 어떻게 해 작성할까요? 필자가 처음EJB의 프로그램을 썼을 때는 , 텍스트 문자 편집기를 사용해EJB를 작성했습니다. 텍스트 문자 편집기를 사용해EJB의 코드를 쓰는 작업은 , 통상의 것JavaBean을 작성하는데 비교하면(자) 매우 큰 일입니다. 예를 들면 , open source의 어플리케이션 서버JBoss상에서 움직이는 간단한CMP을 1개만드는데나 , 아래 표와 같이 복수의 파일이 필요합니다.

파일해설
HelloRemote 리모트 인터페이스
HelloHome 홈 인터페이스
HelloCMPCMP엔티티Bean본체
ejb-jar.xmlEJB용의 배치 기술자
jboss.xmlJBoss용의 배치 기술자
jbosscmp-jdbc.xml JBoss용의 배치 기술자(CMP용)
표1 CMP엔티티Bean를 작성하는데 필요한 파일

 1개의 것Bean을 작성하는데 , 이만큼의 파일이 필요하게 되면(자) , 개발 효율의 저하가 용이하게 예상됩니다. 그리고 , 이러한 파일은 서로 의존하고 있기 (위해)때문에 ,HelloCMP에 메소드를 추가했을 경우 ,HelloHome/HelloRemote등 도 변경할 필요가 있어 , 메인트넌스성도 나빠집니다.

 그리고 ,CMP엔티티Bean를 사용하면(자) ,SQL문장을 이용한JDBC에 의한 코딩을 실시하는 일 없이 데이타베이스를 이용할 수가 있기 (위해)때문에 , 개발이 편해진다고 말해지고 있습니다만 , 그 이야기를 그대로 받아들여CMP엔티티Bean를 이용해 아픈 눈을 당한 (분)편도 안에는 계(오)시는 것은 아닐까요.

 EJB(을)를 이용하고 있는 현장에서는 , 시판의 것IDE을 이용해 개발하고 있는 케이스가 많을 것입니다. 그러나 ,Java Solution포럼으로 간 「제5회 독자 앙케이트:IDE/AP서버/체제의 이용 상황을 총괄」을 본다고 알 수 있도록(듯이) ,Java의 이용자의 상당수는 , 텍스트 문자 편집기를 이용해 코드를 쓰고 있는 것이 현상입니다. 그리고 , 비록IDE을 사용했다고 해도 ,IDE의 기능을 적절히 사용하지 않고 원시 코드에 직접 메소드를 추가하고 있는 동안에 , 파일의 정합성이 잡히지 않게 된다고 하는 일도 있습니다.

 「무엇으로1개의 것EJB을 작성하는데 , 이렇게 많은 파일이 필요하게 되는 것일까? 1개의 것EJB을 작성하는데 ,1개의Java코드로 기술할 수 있으면(자)……」라고EJB의 개발에 종사한 것이 있는 사람이라면 누구든지 한 번은 생각하겠지요. 이 「EJB개발자의 꿈」을 실현해 주는 것이 , 이번 소개하는XDoclet인 것입니다.

 XDoclet(이)란?

 XDoclet(이)란 , 코드 생성 엔진이며 ,Java를 위한 애트리뷰트(attribute) 지향 프로그래밍을 제공하는 것입니다. 애트리뷰트(attribute) 지향과는 , 유저가 특수한JavaDoc태그를 코드중에 기술하는 것 에 의해 , 그 코드에 대한 애트리뷰트(attribute)(속성)을 주어 코드의 자동 생성에 대한 힌트로 합니다. XDoclet(은)는 소스중에 파묻힌 속성으로부터 , 다양한 파일(EJB의 경우 , 홈/리모트 인터페이스 , 각종DD) 을 자동 생성합니다(그림1).

그림 1 XDoclet에 의한 코드 생성

 XDoclet(을)를 사용하는 것으로 , 다음과 같은 이점이 있습니다.

1.복수의 코드에 분산하고 있던 원시 코드를1개의 것JavaBean에 정리할 수가 있어 개발의 효율화와 코드의 대폭적인 삭감을 실시할 수가 있습니다.XDoclet의Web페이지에 의하면 , 약85%의 코드를XDoclet이 자동 생성해 준다고 쓰여져 있습니다.

2.파일이1개가 된 것에 의해 , 메인트넌스의 수고를 큰폭으로 삭감합니다. EJB본체 , 인터페이스 ,DD사이의 부정합이 생기는 일도 없습니다.

3.어플리케이션 서버의 종류나 버젼의 차이에 의해DD가 다릅니다만 ,DD은XDoclet이 자동적으로 생성해 주므로 , 개발자는 , 어플리케이션 서버마다의 차이를 의식할 필요가 없습니다. 그리고 , 어플리케이션 서버 고유의 설정도 ,Ant의 인수나 태그의 코멘트로 간결하게 기술할 수 있습니다.

 본고에서는 ,EJB의 생성에 대한 보고 해설합니다만 ,XDoclet에는Web어플리케이션 ,hibernate/jdo등의O-R매핑 ,JMX의 개발을 지원하는 태스크가 준비되어 있습니다.

태스크기능
ejbdoclet EJB개발 지원
webdoclet Servlet/JSP개발 지원
jdodocletO-R매핑(JDO(*1) ) 개발 지원
hibernatedocletO-R매핑(hibernate(*2))개발 지원
jmxdoclet JMX개발 지원
표2 XDoclet그리고 제공되고 있는 태스크

*1:Java Data Object. JSR-000012(으)로서JCP로 표준화 되고 있는O-R매핑의 표준API. 자세한 것은http://access1.sun.com/jdo/을 참조.
*2:SourceForge그리고 개발되고 있는O-R매핑 툴(http://sourceforge.net/projects/hibernate/)

 그리고 ,XDoclet는 확장성이 뛰어나 코드 생성의 템플릿을 작성하는 것으로써 , 다른 어플리케이션/기술에 대응시킬 수가 있습니다. 확장성의 이점은 겉(표)2에 나타내도록(듯이) 다양한 태스크가 제공되고 있는 것으로부터도 분 빌리고지요.

 덧붙여XDoclet 1.x계에서는 , 이 템플릿에XML베이스로 기술된XDoclet독자적인 구문을 이용하고 있습니다만 ,XDoclet 2.0으로부터Jakarta의 템플릿 엔진Velocity도 이용할 수 있게 됩니다. Velocity(은)는 심플한 구문에 강력한 기능을 갖추고 있어XDoclet의 확장성은 한층 더 향상하겠지요.

 XDoclet(은)는 ,Jakarta프로젝트로 개발되고 있는 것은 아닙니다만 , 벌써 말한 것처럼Ant/Velocity등 과 깊은 관련이 있습니다. 그리고 ,Ant 2.0의proposal디렉토리에는 ,XDoclet이 포함되어 있어Ant 2.0로부터 는 디폴트로 첨부될 것 같습니다.

 XDoclet에 의한EJB생성

 그러면 , 실제로AntXDoclet를 이용해EJB를 작성해 봅시다. 여기에서는 ,JBoss그리고 이용 가능한EJB을XDoclet을 사용해 작성합니다. 작성하는EJB은 , 메일 주소와 이름을 데이타베이스의 테이블로 관리하는CMP엔티티Bean으로 ,MemberBean라고 명명합니다. MemberBean(은)는 , 필드에emailname를 가져 ,email를 프라이머리 키로 합니다. 그리고 , 트랜잭션(transaction)는 , 특히 고려하지 않는 것으로 합니다. 이MemberBean클래스의 샘플을리스트1에 나타냅니다.

리스트1 MemberBean.java
package test.ejb;

import javax.ejb.*;
import java.util.Collection;

/** 
   * EJB전체에 대한 설정
 * @ejb.bean
 *  type="CMP"             EJB의 타입
 *  name="Member"          EJB의 이름
 *  primkey-field="email"  프라이머리 키필드
 *  view-type="remote"     뷰타이프
 * 
 * @ejb.finder             finder
 *  signature="Collection findAll()"
 *  transaction-type="NotSupported"
 * @ejb.persistence
 *  table-name="member"    O-R매핑을 실시하는 테이블명
*/
public abstract class MemberBean 
extends BaseEntityBean implements EntityBean {

/**
 * getEmail메소드(email필드)에 대한 설정
 *
 * @ejb.interface-method    리모트 인터페이스를 생성
 * @ejb.pk-field            이 메소드에 대응하는 필드를
 *                          프라이머리 키로 지정
 * @ejb.persistence 데이타베이스의 테이블과의 매핑의 지정
 *  column-name="email"     컬럼명
 *  sql-type="VARCHAR"      SQL의 타입
 */
 public abstract String getEmail();

 /**
 * setEmail메소드에 대한 설정
 *
 * @ejb.interface-method    리모트 인터페이스를 생성
 */
 public abstract void setEmail(String email);

 /**
 * getName메소드(name필드)에 대한 설정
 *
 * @ejb.interface-method   리모트 인터페이스를 생성
 * @ejb.persistence        데이타베이스의 테이블과의 매핑의 지정
 *  column-name="name"     컬럼명
 *  sql-type="VARCHAR"     SQL의 타입
     */

public abstract String getName();
    /**
     * setName메소드에 대한 설정
     *
     * @ejb.interface-method 리모트 인터페이스를 생성
     */
   public abstract void setName(String name);

    /**
     * EJB의 생성에 대한 설정
     *
     * @ejb.create-method create메소드를 생성
     */
    public String ejbCreate(String email)
 throws CreateException {
        setEmail(email);
        return null;
    }

}

※적자 부분은 주석이며 , 실제의 리스트에는 포함되지 않습니다.
주:여기에서는 , 코드를 알기 쉽게 하기 위해서 , 엔티티Bean에 공통된 메소드를 실장한 추상 클래스BaseEntityBean를 준비해 , 그 클래스를 계승해MemberBean를 실장하고 있습니다.

 XDoclet(을)를 이용하는 경우 , 코멘트에 묻은 태그(리스트 1의 청자 부분)에 의해 ,EJB의 속성을 지정합니다. 이 속성은 , 클래스 코멘트와 메소드 코멘트에 의해 , 각각 다음과 같이 지정 항목이 다릅니다.

●클래스 코멘트
EJB의 타입 ,EJB이름 , 테이블명 , 프라이머리 키필드의 지정 등 ,EJB전체에 걸치는 속성을 지정합니다.

●메소드 코멘트
O-R매핑을 실시할 때의 , 데이타베이스의 테이블의 컬럼과 필드의 대응 붙여 그 메소드에 대한 리모트 인터페이스를 생성하는지 어떤지 ,create메소드를 생성하는지 어떤지 등 , 메소드에 대한 속성을 지정합니다.

 덧붙여XDoclet의EJB생성에 사용할 수 있는 태그에 대한 자세한 것은 ,Tag Reference for @ejb사이트를 봐 주세요.

■Ant의 빌드 파일

 XDoclet의 태스크는 , 처음은Ant에는 포함되어 있지 않습니다. 제공되어 있지 않은 태스크를 이용하려면 ,taskdef태스크를 이용해 , 다음과 같이 태스크를 정의할 필요가 있습니다.

<taskdef name="ejbdoclet"
classname="xdoclet.modules.ejb.EjbDocletTask"
classpathref="class.path"/>

 여기에서는 ,xdoclet.modules.ejb.EjbDocletTask클래스를ejbdoclet태스크로서 정의합니다. XDoclet에 한정하지 않고 , 확장 태스크를 이용하는 경우에는 ,taskdef을 이용하므로 , 기억해 두면(자) 좋을 것입니다. 정의한ejbdoclet태스크를 이용하려면 , 다음과 같이 기술합니다.

<ejbdoclet
    destdir="${gen-src.dir}" 생성된 코드를 출력하는 디렉토리
    excludedtags="@version,@author,@todo" 처리 대상에서 제외하는 태그
    ejbspec="2.0" EJB사양의 버젼
>


<!-- 원시 파일의 지정 -->
<fileset dir="${src.dir}">
    <include name="test/ejb/*Bean.java"/>
</fileset>

<!-- CMP엔티티Bean생성 -->
<entitycmp/>

<!-- 리모트/홈 인터페이스 생성 -->
<remoteinterface/>
<homeinterface/>

<!-- ejb-jar.xml의 생성 -->
<deploymentdescriptor
    destdir="${meta-inf.dir}" DD을 생성하는 디렉토리
/>

<!-- JBoss고유의 것DD의 생성 -->
<jboss
    version="3.0" JBoss의 버젼
    destdir="${meta-inf.dir}" DD을 생성하는 디렉토리
/>

</ejbdoclet>

※적자 부분은 주석이며 , 실제의 리스트에는 포함되지 않습니다.

  그런데 , 실제로 ,XDoclet라고JBoss를 이용해 , 샘플 프로그램으로부터EJB를 생성해 이용해 봅시다.

 인스톨과 실행

 Ant(을)를 전회의 기사에 따라 , 인스톨 해 두어 주세요. 그리고 ,J2SDK(1.4.1_01) ,JBoss,XDoclet를 아래와 같은 디렉토리에 인스톨 합니다.

JBoss C:\jboss-3.2.0RC1_tomcat-4.1.18
XDocletC:\ xdoclet
XDocletC:\j2sdk1.4.1_01
주:본고에서는 ,Windows을 대상으로 기술하고 있습니다만 , 패스명을 변경하면 ,UNIX그렇지만 이용 가능합니다. 그리고 , 다른 디렉토리에 인스톨 한 사람은 적당 읽어 바꾸어 주세요.

●XDoclet의 인스톨
 http://sourceforge.net/project/showfiles.php?group_id=31602(으)로부터 ,XDoclet-bin-1.2b2.zip를 다운로드해 주세요. C:\xdoclet디렉토리를 작성해 , 이 디렉토리에 해동해 주세요.

●JBoss의 인스톨
 http://sourceforge.net/project/showfiles.php?group_id=22866(으)로부터jboss-3.2.0RC1_tomcat-4.1.18.zip 를 다운로드해 주세요. 다운로드한zip파일을 ,C:\디렉토리에 그대로 해동해 주세요.

 XDoclet(와)과JBoss의 인스톨이 완료하면(자) ,C:\jboss-3.2.0RC1_tomcat-4.1.18디렉토리(이하 ,JBOSS_HOME라고 기술) 와C:\XDoclet디렉토리에 각각의 파일이 인스톨 되고 있는 것을 확인해 주세요. 다른 디렉토리에 인스톨 했을 경우(특히UNIX를 이용하는 (분)편 ) 나 , 다른 버젼의 것JBoss을 이용하는 (분)편은 적당 읽어 바꾸어 주세요.

  이번 소개한 샘플을여기로부터 다운로드해 ,C:\디렉토리에 해동해 주세요. 샘플에는 , 다음의 파일이 포함됩니다.

파일설명
build.xml Ant의 빌드 파일
build.properties 프롭퍼티 파일(build.xml로부터
인클루드 된다. JBoss(을)를XDoclet의 패스
로 설정한다 )
src/test/ejb/MemberBean.javaXDoclet로 이용하는EJB의 원시 코드
src/TestClient.java테스트용의 코드

 만약 ,XDoclet이라고JBoss를 상기로 지정한 디렉토리와 다른 디렉토리에 인스톨 했을 경우에는 ,build.properties파일에 포함되는jboss과XDoclet의 루트 디렉토리의 패스를 변경해 주세요. 이것으로 준비는 완료입니다.

그런데 ,Ant를 사용해 ,EJB를 생성해 봅시다. C:\XDoclet-sample디렉토리로 이동해 ,

C:\xdoclet-sample> ant

라고 실행하면(자) ,EJB가 생성됩니다. Ant(을)를 실행하면(자) , 우선 ,ejbdoclet타겟으로보다 ,src/test/ejb/MemberBean.java에 있는MemberBean.java으로부터 다음의 파일이 생성됩니다.

build/gen-src/test/ejb/MemberCMP.java CMP엔티티Bean본체
build/gen-src/test/ejb/Member.java 리모트 인터페이스
build/gen-src/test/ejb/MemberHome.java홈 인터페이스
build/meta-inf/ejb-jar.xmlEJB의 표준DD
build/meta-inf/jboss.xmlJBoss고유의DD
build/meta-inf/jbosscmp-jdbc.xml JBoss고유의DD(CMP용)

 단1개의 것MemberBean.java으로부터EJB에 필요한 파일이 차례차례로 생성되는 모양은 마치 마법 같아 , 확실히 하마이오니절의 부엌입니다. 이러한 파일은 , 컴파일 되어 ,dest/xdoclet-sample.jar에EJB의jar파일로서 생성됩니다.

 동작 확인

 xdoclet-sample.jar(을)를 ,JBoss을 인스톨 한 디렉토리아래의server/default/deploy디렉토리에 카피해 ,JBOSS_HOME\bin\run.bat파일을 실행(더블 클릭 혹은 , 커멘드 라인으로부터 실행)합니다. 이것으로 ,JBoss가 기동합니다.

 무사JBoss가 기동하면(자) , 작성했다EJB를 테스트하는 클라이언트를 실행해 봅시다. c:\xdoclet-samples\build\classes디렉토리로 이동해 , 다음과 같이 실행합니다 (여기에서는J2SDK 1.4의 경우입니다만 ,J2SDK 1.3환경에서 올바르게 동작하지 않는 경우는 ,C:\jboss-3.2.0RC1_tomcat-4.1.18\client이하의jar파일 모두에게 클래스 패스를 통해 주세요 ).

C:\>cd C:\xdoclet-sample\build\classes
C:\>SET CLASSPATH=C:\jboss-3.2.0RC1_tomcat-4.1.18\client\jbossall-client.jar;


C:\xdoclet-sample\client>java TestClient create okamototk@nttdata.co.jp "Takashi Okamoto" 
[create]
email:okamototk@nttdata.co.jp
name :Takashi Okamoto


C:\xxdoclet-sample\client>java TestClient find okamototk@nttdata.co.jp
[find]
email:okamototk@nttdata.co.jp
name :Takashi Okamoto


C:\xdoclet-sample\client>java TestClient remove okamototk@nttdata.co.jp
[remove]
email:okamototk@nttdata.co.jp
name :Takashi Okamoto

 당연히 , 등록한 멤버는 , 데이타베이스에 영속화 되고 있으므로 , 멤버를 삭제하지 않는 한 ,JBoss을 일단 정지해 재기동해도 검색할 수 있습니다.

 XDoclet의 유연성

 XDoclet(을)를 이용하면(자) , 간단하게EJB를 작성 가능할 뿐만 아니라 ,JavaBean의 태그나ejbdoclet의 옵션을 조금 변경하는 것만으로 ,EJB의 설정을 간단하게 바꿀 수가 있습니다. 여기에서는 , 방금전의 샘플을 기본으로 해 , 데이터 소스의 지정 , 로컬EJB의 작성 ,WebLogic용무의 것DD의 작성을 통해 ,XDoclet의 유연성을 봅시다.

■데이터 소스를 지정

 샘플에서는 ,JBoss의 디폴트의 데이터 소스(java:/DefaultDS) 를 이용하게 되어 있습니다만 , 명시적으로 데이터 소스를 지정하려면 , 다음과 같이ejbdoclet태스크에valueobject태그를 포함해jboss태그에 데이터 소스를 지정합니다.

<!-- Value오브젝트 생성 -->
<valueobject />

<!-- JBoss고유의 것DD의 생성 -->
<jboss
  version="3.0"
  destdir="${meta-inf.dir}"
  datasource="java:/PostgreSQLDS"
데이터 소스명
  datasourceMapping="PostgreSQL"
데이타베이스 타입
/>
주:샘플에서는 ,Value오브젝트를 생성합니다만 , 데이터 소스의 설정을 하기 위해서 필요하고 있습니다. Value오브젝트를 생성하는 경우는 ,MemberBean.java그리고@ejb.value-object name="Member"로 합니다.

■로컬EJB의 작성

 이번은 , 통상의 것CMP엔티티Bean을 작성했습니다만 ,XDoclet을 이용하면(자) ,EJB 2.0로부터 서포트된 로컬EJB도 간단하게 작성할 수가 있습니다. 쿠라스타그의 아래와 같은view-type

@ejb.bean

...

view-type="remote"
뷰타이프

을 다음과 같이local로 변경합니다.

view-type="local" 뷰타이프

 그리고 ,build.xml의 아래와 같은 부분

<remoteinterface/>
<homeinterface/>

<localinterface/>
<localhomeinterface/>

과 로컬 인터페이스를 생성하도록(듯이) 변경하는 것만으로 , 로컬EJB을 작성할 수가 있습니다. 생성되는 파일의 이름도 ,Member(리모트 인터페이스) 가MemberLocal(로컬 인터페이스) 에 ,MemberHome(홈 인터페이스) 가MemberLocalHome(로컬 홈 인터페이스)로 바뀝니다.

 view-type(을)를both로 해 , 리모트EJB의 설정과 로컬EJB의 설정을 쓰는 것으로 , 양쪽 모두의 것EJB을 동시에 생성할 수도 있습니다.

■WebLogic용무의 것DD을 작성

 WebLogic용무의 것DD을 작성하려면 ,ejbdoclet태스크중에jboss태그 대신에weblogic태그를 정의합니다.

<weblogic
  version="6.1"
  destdir="${samples.meta-inf.dir}"
  datasource="java:/samplesDataSource"
  persistence="weblogic"
/>

 jboss태그와weblogic태그를 동시에 정의하면 ,JBoss용무와WebLogic용무의 것DD이 생성되므로 ,2개의 어플리케이션 서버에 대응했다EJB를 작성하는 것이 가능합니다. 그 외 ,WebSphere(websphere태그)/JOnAS(jonas태그)/JRun(jrun태그) 등에 대응하고 있습니다. 이와 같이 간단하게 다른 어플리케이션 서버용의 것DD을 작성할 수가 있습니다.

 보시는 바와 같이 ,XDoclet를 이용하면(자) ,ejbdoclet태스크의 파라미터나JavaBean의 태그를 변경하는 것만으로 유연하게EJB의 생성 룰을 바꿀 수가 있습니다.

 통계

 이번은 , 응용편이라고 하는 것으로 ,XDoclet를 이용했다EJB의 작성 방법에 대해 소개했습니다. XDoclet(을)를 이용하면(자) , 고가의IDE를 구입하는 일 없이 ,EJB의 개발을 간단하게 실시할 수 있게 됩니다. 확실히 혁명적이라고 해도 좋을 것입니다. 지금까지EJB를 경원하고 있던 (분)편도 ,XDoclet를 사용해 ,EJB에 접해 보면 어떻습니까?

필자 프로필
오카모토 타카시(오카모토 타카시)

현재 , 주식회사NTT데이터 기술개발 본부에 소속. Web서비스 기술의 연구개발에 종사한다. 개인에서는 ,Debian/GNU Linux을 시작으로 한 다양한 open source 프로젝트에 콘트리뷰트 하고 있어 ,Jakarta그럼 , 프로덕트의 국제화/일본어에의 로컬라이즈를 중심으로서 활동하고 있다.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:14

제2회 Ant그리고Java의 빌드를 간단하게 하는


오카모토 타카시
NTT데이터 기술개발 본부/Ja-Jakarta Project
2003/1/16


 이번은 ,Java Solution포럼으로 간 「제7회 독자 조사:옆의 회사는Jakarta Project을 활용하고 있어? 」(으)로 ,Tomcat에 이어 이용자가 많았던 빌드 툴의 것Ant을 채택합니다. Ant에 대해서는 , 기본편과 응용편의2회로 나누어 소개합니다. 기본편이 되는 이번은 ,Ant의 개요와 기본적인 사용법에 대해 해설합시다.

 빌드 툴이라고 하면(자) ,make를 떠올리는 (분)편도 많다고 생각합니다만 ,Ant의 빌드 파일은XML으로 기술한다고 하는 특징이 있어 ,Java라는 친화성도 높아지고 있습니다. 전술의 독자 조사에 의하면 , 현재 이용중의 개발툴로서 「에디터+JDK」가 가장 많았습니다만 , 확실히 이 개발 패턴은Ant을 사용하는 것이 왕도라고 할 수가 있겠지요.

 Borland JBuilder등의IDE를 사용하고 있는 (분)편은 , 「IDE을 사용해 빌드 하면Ant은 필요없는 것이 아닌가?」라고 생각될지도 모릅니다. 그러나 , 예를 들면 ,Windows환경에서 개발을 실시해 , 동작 확인을UNIX위에서 실시한다고 하는 크로스 카이하츠를 실시하고 있는 경우 , 원시 코드의 불편을 찾아낼 때마다Windows위에서 빌드 해 ,UNIX환경에 카피해 동작 확인한다……라고 한 시간이 드는 작업을 반복하고 있을 것은 없습니까? 동작 확인을 행하기 위한 머신이 네트워크에 접속되고 있는 경우는 아직 좋습니다만 , 그렇지 않은 경우는 플로피 디스크나CD-R에 기입하는 등 , 수고가 배증합니다.

 이러한 때에도 ,Ant를 이용하는 것으로써 ,JDK가 인스톨 된 환경이면 , 간단하게 소스로부터 빌드 할 수가 있어 파일 카피의 수고를 줄일 수 있고 개발 효율이 오릅니다. 그리고 , 최근의 것IDE은Ant을 서포트하고 있는 것도 많기 때문에 ,Ant그리고 작성해 두면 , 다른IDE위에서 간단하게 빌드 할 수도 있습니다.

 Ant(을)를 사용해 보자

 「백문은 일견 밖에않고」라고 하는 것으로 , 우선은Ant을 사용해 봅시다. Ant(을)를Jakarta의 사이트http://www.apache.org/dist/ant/binaries/로부터 다운로드해 , 적당한 디렉토리에 해동해 주세요. 필자의 환경에서는 , 집필시의 최신 버젼1.5.1을/usr/local/jakarta-ant-1.5.1(Windows의 경우는 ,c:\usr\local\jakarta-ant-1.5.1) 에 전개했습니다.

 그럼 , 샘플을 다운로드해 실제로 시험해 봅시다. 여기로부터 다운로드해 주세요.

 Ant의 환경 설정을 실시합니다. UNIX환경의 경우 , 환경 변수JAVA_HOME에JDK의 인스톨 디렉토리를 ,ANT_HOME에Ant의 인스톨 디렉토리를 설정해 ,PATH를$ANT_HOME/bin에 통하면 준비는 완료입니다. 준비를 할 수 있으면(자) , 아래와 같이 샘플을 전개해 ,ant커멘드를 실행해 주세요.

●UNIX(bash) 의 경우
$ export ANT_HOME=/usr/local/jakarta-ant-1.5.1
$ export JAVA_HOME=/usr/local/j2sdk1.4.1_01
$ export PATH=/usr/local/jakarta-ant-1.5.1/bin:${PATH}
$ jar xvf hello-ant.zip
$ cd hello-ant
$ ant
Buildfile: build.xml

compile:
compile타겟의 실행
[mkdir] Created dir:
/tmp/hello-ant/classes
[javac] Compiling 2 source files to
/tmp/hello-ant/classes


jar: jar타겟의 실행
[jar] Building jar:
/tmp/hello-ant/foo-1.0.jar

 최초의 환경 변수의 설정은 ,.bashrc등에 써 두면(자) 좋을 것입니다.

 Windows의 환경의 경우는 , 유저 환경 변수의JAVA_HOME,ANT_HOME,PATH를 각각 , 아래와 같이 설정합니다.

JAVA_HOME c:\j2sdk1.4.1_01(J2SDK의 인스톨 디렉토리)
ANT_HOMEc:\usr\local\jakarta-ant-1.5.1
PATHc:\usr\local\jakarta-ant-1.5.1\bin

 환경 설정을 할 수 있으면(자) , 이하와 같이ant커멘드를 실행해 주세요. 다음과 같은 실행 결과를 얻을 수 있을 것입니다.

●Windows의 경우
C:\tmp> jar xvf hello-ant.zip
C:\tmp> cd hello-ant
C:\tmp\hello-ant> ant
Buildfile: build.xml

compile: compile타겟의 실행
[mkdir] Created dir: C:\tmp\hello-ant\classes
[javac] Compiling 2 source files to C:\tmp\hello-ant\classes


jar: jar타겟의 실행
[jar] Building jar: C:\tmp\hello-ant\foo-1.0.jar

BUILD SUCCESSFUL
Total Time: 7 seconds

 이것으로 ,Java소스의 컴파일과jar파일foo-1.0.jar의 작성을 했습니다. Ant(은)는 실행한 커런트 디렉토리에 있는 빌드 파일build.xml을 읽어들여 , 빌드를 실행합니다. 이 예에서는 , 다음에 소개하는 빌드의 실행 단위 「타겟」의 디폴트치가jar되고 있으므로 ,jar타겟의 실행과jar타겟으로 의존한compile타겟을 실행하고 있습니다.

 그럼 ,classes디렉토리아래의 것Foo1.class을 실행 배어 봅시다. 이하의 실행 결과를 얻을 수 있을 것입니다.

●Windows의 경우
C:\tmp\hello-ant> cd classes
C:\tmp\hello-ant>classes>java Foo1
안녕하세요Ant

 디폴트에서는 실행됩니다만 , 샘플에는javadoc의 생성 프로세스도 포함하고 있기 (위해)때문에 , 다음과 같이javadoc타겟을 지정해 실행하면(자) ,api디렉토리아래에JavaDoc가 생성됩니다.

ant javadoc

그리고 ,build.xml이외의 빌드 파일을 이용하고 싶은 경우는 ,-f옵션으로 ,

ant -f [빌드 파일명]

으로 , 빌드 파일을 지정할 수도 있습니다.

 Ant의 특징

 Ant의 구체적인 해설에 들어가기 전에 ,Ant의 특징에 대해 정리합시다. Ant(을)를 사용하면(자) 이하와 같은 메리트가 있습니다.

(1) 복잡한 컴파일 프로세스를 간략화 가능
 Ant(을)를 이용하면(자) , 컴파일 ,jar파일 작성등의 빌드 순서를 형식화할 수 있으므로 , 복잡한 빌드 순서를 간략화할 수 있습니다. 반대로 , 간단한 빌드 커멘드로 컴파일 할 수 있는 경우(예를 들면 , 원시 파일이1개 밖에 없고 ,javac커멘드를1회 실행하는 것만으로 컴파일 가능한 경우등 ) 는 , 빌드 파일의 작성에 시간이 들어 , 오히려 작업 부하가 증가합니다.

(2) 풍부한Java개발툴에의 대응
 javac,jar,rmic,war,native2ascii등의 커멘드의 실행에 대응하고 있습니다. Ant그럼 , 각각의 커멘드를 실행하기 위한 「태스크」라고 불리는 클래스가 준비되어 있습니다. 그리고 , 최근에는 ,JavaBeans으로부터EJB를 생성하는xdoclet/vdoclet등 ,Jakarta이외로부터도 태스크가 제공되고 있습니다.

(3) 처리 프로세스의 그룹화
 소스의 컴파일이나jar파일의 생성 ,javadoc생성 등 , 있는 결정된 처리를 타겟으로 정리할 수가 있습니다. 예를 들면 ,javac태스크와native2ascii태스크를 정리해 컴파일 타겟으로 할 수가 있습니다. 타겟으로보다 , 그때그때로 필요하게 되는 처리만을 간단하게 선택해 실행할 수 있게 되어 있습니다.

(4) 의존관계(dependencies)의 해결
 예를 들면jar파일의 작성 전에는 , 소스의 컴파일이 필요합니다. Ant(은)는 이러한 처리의 의존관계(dependencies)을 해결할 수 있습니다. 그 때문에jar파일을 작성하기 전에 , 소스가 컴파일 되고 있는지 아닌지를 의식할 필요가 없어집니다.

(5) 인크리멘탈인 빌드
 빌드 처리를 실시하는 태스크는 , 기본적으로 타임 스탬프를 조사해 처리가 필요한 파일만 처리합니다. 예를 들면 , 한 번 소스를 컴파일 한 다음에 소스를 변경했을 경우 , 변경이 있던 소스만을 재컴파일 할 수가 있습니다.

 Ant의 사용법을make과 비교해 이해한다

 그럼 ,make그리고 이용하는Makefile과Ant의 빌드 파일을 대비하면서 ,Ant의 사용법을 보고 갑시다. 우선은 ,make그리고 사용하는Makefile(리스트1)를 봐 주세요 (덧붙여 리스트1와 리스트2는 기능마다 분류 하고 있습니다 ).

빨강변수 설정
파랑jar파일의 생성
초록컴파일 실행
보라색javadoc생성
회색파일의 클린 업

리스트1 Makefile의 예
######## 변수의 설정 ########

# 원시 파일의 일람
BUILD_SRC=src/foo1.java src/pkg/foo2.java

# 원시 파일로부터 생성되는 클래스 파일
BUILD_DEST=classes
BUILD_FILES=$(BUILD_DEST)/foo1.class $(BUILD_DEST)/pkg/foo2.class

# JavaDoc파일
JAVADOC_DEST=api
JAVADOC_FILE=$(JAVADOC_DEST)/foo1.html $(JAVADOC_DEST)/pkg/foo2.html

# jar파일
JAR=foo.jar


## jar파일 생성

jar:$(JAR)
$(JAR):$(BUILD_FILES)
    jar cvf   $(JAR) -C $(BUILD_DEST)/ .


## 컴파일 실행
compile:$(BUILD_FILES)
$(BUILD_FILES):$(BUILD_SRC)
    mkdir -p $(BUILD_DEST)
    javac -d $(BUILD_DEST) $(BUILD_SRC)


## javadoc생성
javadoc:$(JAVADOC_FILE)
$(JAVADOC_FILE): $(BUILD_SRC)
    javadoc -d api $(BUILD_SRC)

## 파일의 클린 업
clean:
    rm -fr $(BUILD_DEST) $(JAVADOC_DEST)

 이Makefile를Ant의 빌드 파일(build.xml) 에 고쳐 쓰면(자) 다음과 같이 됩니다 (방금전 실행했다Build.xml를 조금 간략화하고 있습니다 ).

리스트2 Ant의 빌드 파일(build.xml)
<?xml version="1.0" encoding="Windows-31J"?>

<project name="foo" default="jar" basedir=".">


 
<!-- 원시 파일의 디렉토리 -->
  <property name="build.src" value="src"/>

  <!-- Class파일을 출력하는 디렉토리 -->
  <property name="build.dest" value="classes"/>

  <!-- Javadoc 를 출력하는 디렉토리 -->
  <property name="javadoc.dest" value="api"/>

  <!-- 프로젝트명 -->
  <property name="project" value="foo"/>

  <!-- 버젼 번호 -->
  <property name="version" value="1.0"/>

  <!-- 출력jar파일 -->
  <property name="buuld.jar" value="${project}-${version}.jar"/>



 
<!-- jar파일 작성 -->
  <target name="jar" depends="compile">
    <jar
          jarfile="${build.jar}"
          basedir="${build.dest}">
    </jar>
  </target>



 
<!-- 컴파일 실행 -->
  <target name="compile">
    <mkdir dir="${build.dest}" />
    <javac srcdir="${build.src}"
           debug="${debug}"
   destdir="${build.dest}"
    />
  </target>



 <!-- Javadoc작성 -->
  
<target name="javadoc">
    <javadoc
      packagenames="*"
      sourcepath="${build.src}"
      destdir="${javadoc.dest}">
    </javadoc>
  </target>



 <!-- 파일의 클린 업 -->
  
<target name="clean">
    <delete dir="${build.dest}" />
    <delete file="${build.jar}" />
  </target>


</project>

 가장 큰 차이로서Makefile는 커멘드의 나열로 기술되고 있습니다만 ,Ant의 빌드 파일은XML으로 기술되고 있습니다. 구체적으로 기능 마다 차이를 봅시다.

디폴트의 처리의 지정

 여기서의 디폴트의 처리란 , 옵션의 지정을 생략 했을 때 ,Ant라고Make가 무슨 처리를 실시하는지 , 라고 하는 것을 가리킵니다. 빌드 커멘드를 실행했을 때의 디폴트의 처리는 ,Make의 경우 , 제일 처음에 기술된 처리가 됩니다만 ,Ant그럼project태그로 지정합니다. 상기의 예에서는 ,jar파일 작성을 디폴트의 처리로 하고 있습니다. Makefile파일에서는jar:$(JAR)의 부분이 ,Ant의 빌드 파일에서는<project .. default="jar"…>의 부분이 해당합니다.

변수의 설정

 Ant도Make도 빌드 파일 중(안)에서 자유롭게 참조 가능한 변수를 선언할 수가 있습니다. make(은)는 ,Makefile의 선두의 블록으로 이하와 같이 변수 설정을 실시합니다.

변수명=치

 그리고 , 이하와 같이 변수의 참조를 실시합니다.

$(변수명)

 이것에 대해서 ,Ant는 , 이하와 같이property태그를 사용해 , 변수(Ant에서는 변수를 통상 프롭퍼티라고 부릅니다 ) 의 설정을 실시합니다.

<property name="변수명" value="치"/>

 그리고 , 이하와 같이 변수의 참조를 실시합니다.

${변수명}

커멘드의 실행

 make그럼 , 예를 들면 아래와 같은 기술을 보면 알 수 있도록(듯이) ,mkdir,javac등의 쉘 커멘드를 직접 호출해 처리를 진행시킵니다.

$(BUILD_FILES):$(BUILD_SRC)
  mkdir -p $(BUILD_DEST)
    javac -d $(BUILD_DEST) $(BUILD_SRC)

 이것에 대해서Ant에서는 , 전술한 것처럼javac,jar,javadoc등 , 실제로 실행하는 커멘드에 대응 지을 수 있었던 태스크를 실행하는 것으로 처리를 진행시켜 나갑니다. 태스크는 , 다음과 같이 표기합니다.

<태스크명 인수(속성)="치" …>
    <인수(요소)>치</인수(요소)>
</태스크명>

 인수는 , 각 태스크 고유의 값을 받습니다만 , 예를 들면 ,mkdir,delete,copy등의 파일 조작을 실시하는 태스크는 ,dir(디렉토리를 지정한다 ) 등의 공통 인수를 지정할 수 있습니다. 그리고 , 인수는 , 속성과 요소의2종류를 지정할 수 있습니다만 , 기본적으로는 속성에 인수를 설정합니다. 파일의 집합 , 클래스 패스 등은 인수로 지정할 수도 있습니다. 태스크의 구체적인 사용법에 대해서는 , 자세하게 후술 합니다.

의존관계(dependencies)의 기술

 빌드를 실시할 때 ,jar파일의 작성 전에는 , 소스의 컴파일이 필요등의 의존관계(dependencies)가 발생합니다만 ,make도Ant도 의존관계(dependencies)를 기술할 수가 있습니다. Makefile그럼 ,

생성 파일:중간 파일
  커멘드2  # 중간 파일로부터 생성 파일을 생성하는 커멘드
  (커멘드2')  # 복수의 커멘드를 실행하고 싶은 경우 , 개행해 기술

중간 파일:원시 파일
  커멘드1  # 원시 파일로부터 중간 파일을 생성하는 커멘드
  (커멘드2') # 복수의 커멘드를 실행하고 싶은 경우 , 개행해 기술

이라고 하는 형식에서 , 의존관계(dependencies)를 기술합니다. make(은)는“:”보다 좌측의 파일의 타임 스탬프와 우측의 파일의 타임 스탬프를 조사해 , 왼쪽에서(보다) 오른쪽의 타임 스탬프가 새로운 경우 , 혹은 , 좌측의 파일이 존재하지 않는 경우 , 커멘드를 실행합니다. 예를 들면 ,

$(JAR):$(BUILD_FILES)
  jar cvf  $(JAR) -C $(BUILD_DEST)/ .

$(BUILD_FILES):$(BUILD_SRC)
  mkdir -p $(BUILD_DEST)
  javac -d $(BUILD_DEST) $(BUILD_SRC) 

에 주목하면(자) ,src/foo1.java,src/pkg/foo2.java ($(BUILD_SRC))에 대해서 ,mkdir/javac가 실행됩니다. 생성된 클래스 파일classes/foo1.class classes/pkg/foo2.class ($(BUILD_FILE))에 대해서jar가 실행되어foo.jar ($(JAR))가 생성됩니다.

 각 스텝은 , 처리의 필요가 없으면 스킵 됩니다.

주:“jar:$(JAR)”이라고 하는 행은 ,make jar 커멘드로jar파일을 생성하기 위한 굳이 실행하지 않는 더미행입니다.

 이것에 대해서 ,Ant는 , 타겟으로 대해 의존관계(dependencies)를 기술해 정의합니다.

<target name="타겟1">
  태스크2
</target>

<target name="타겟2" depends="타겟1">
  태스크1
</target>

 이 예는 ,Ant이 타겟2을 실행하려고 하면(자) , 타겟1이 실행이 끝난 상태인지 어떤지 조사해 ,

미실행의 경우
  타겟1을 실행한 후 , 타겟2을 실행

실행 끝난 경우
  타겟1을 실행하지 않고 , 타겟2을 실행

이라고 하는 동작을 합니다. 여기서 주의가 필요한 것은 ,make은 , 생성된 파일과 소스의 타임 스탬프를 조사해 의존관계(dependencies)를 처리합니다만 , 타겟은 「현재 실행되고 있는Ant으로 벌써 처리되었는지 어떠했는지」를 조사해 파일의 타임 스탬프를 조사하고 있지 않습니다. 즉 ,Ant을 실행할 때 마다 각 타겟은 실행됩니다. 실제로 파일의 타임 스탬프를 조사해 커멘드를 실행할지 어떨지는 , 태스크가 판별합니다.

 Ant의 이점

 그런데 , 여기까지의 이야기를 읽으면(자) , 언뜻 봐 , 「할 수 있는 것은Makefile것과 같지만 ,XML그리고 기술해야 하는 분 ,ant(분)편이 귀찮지?」라고 생각되는 (분)편도 있을지도 모릅니다. 여기까지의 내용을 정리하면(자) ,Ant를 사용하면(자) 다음과 같은 메리트가 있습니다.

(1) 파일의 의존관계(dependencies)를 기술할 필요가 없어진다
 태스크가 주어진 각 소스와 생성되는 파일의 타임 스탬프를 조사해 , 처리가 필요한 소스만을 처리해 줍니다. Makefile(와)과 같이1개1개 파일을 지정해 의존관계(dependencies)를 기술할 필요는 없습니다.

(2) 처리 대상이 되는 파일을 개별적으로 기술할 필요는 없다
 Makefile그럼 ,SRC=foo1.java foo2.java과 같이 개별적으로 파일을 지정할 필요가 있었습니다. 이것에 대해서Ant에서는 ,srcdir="..."과 같이 디렉토리를 지정하면 , 그 디렉토리 이하의 모든 파일을 처리 대상으로 해 줍니다. Makefile그렇지만find을 이용하면 , 어느 디렉토리 이하의 것 *.java에 성냥 하는 파일등을 그룹화 할 수가 있습니다만 , 거기에 비교해도 시간이 들지 않습니다.

 다만 , 상기의 메리트는 ,Java의 어플리케이션의 빌드시에 받게 되는 혜택으로 ,C언어의 개발등으로는 ,task으로부터 커멘드 호출을 실시하는 만큼 되어 버려 ,Ant의 혜택은 받을 수 없기 때문에 주의해 주세요.

 커멘드를 실행하는 「태스크」의 사용법

 다음에 ,Ant의 요점인 태스크 중(안)에서 , 특별히 중요하다고 생각해지는javac,jar,javadoc,war,echo에 임해서 소개합니다. 인수에 대해서는 , 잘 사용되는 것을 발췌해 게재하고 있습니다. 보다 상세를 알고 싶은 경우는Jakarta프로젝트의태스크 일람을 참조해 주세요. 일본어에 의한 정보를 갖고 싶은 경우는 ,Ja-Jakarta에 의한 일본어 번역을 봐 주세요.

●javac태스크

 javac(은)는 그 이름대로 ,javac커멘드를 태스크로 한 것으로 ,겉(표)1에 나타내는 인수를 가집니다.

표1 javac태스크의 인수
인수기능 필수인가? (디폴트치)
srcdirjava파일의 장소YES
destdir클래스 파일을 출력하는 장소YES
classpathref 미리path로 설정한 패스를 지정 NO
encoding Java파일의 인코딩 NO(시스템의 인코딩)
excludes컴파일 대상으로부터 제외하고 싶은 파일의 리스트 NO
debug 디버그용으로 컴파일 하는지 어떤지 판단하는 플래그NO(false)
※true/false를 지정한다

 클래스 패스에jar파일을 몇개인가 포함하고 싶은 경우는 ,path태스크와 조합해 이용하면(자) 편리합니다. 예를 들면 ,lib디렉토리아래의jar파일에 모두 클래스 패스를 통해 , 서브 렛 클래스를 위해서(때문에)Tomcat혹은WebLogic의jar파일에 클래스 패스를 통해 컴파일을 실시하려면 , 다음과 같이 기술합니다.

<!-- javac그리고 이용하는 클래스 패스의 설정 -->
<path id="myclasspath">
  <!-- lib디렉토리 이하의 것include으로 지정한 파일을myclasspath
          에 포함한다. --> 
  <fileset dir="lib">
    <!-- 모든jar파일을 클래스 패스에 포함하는 -->
    <include name="*.jar"/>
  </fileset>

  <!-- 각 어플리케이션 서버 고유의 것
       jar을myclasspath에 포함하는
       (특정의jar파일을 지정하는 경우는 ,pathelement을 이용)-->
  <pathelement path="${catalina.home}/commons/lib/servlet.jar"/>
  <pathelement path="${weblogic.home}/server/lib/weblogic.jar"/>
</path>
..
<!-- 설정했다myclasspath를 이용해 컴파일 -->
<javac  srcdir="src/java"
        excludes="**/bak/*"
        destdir="build/classes"
        encoding="Windows-31J"
        classpathref="myclasspath" />

 path의 설정은 ,javac을 실행하는 타겟내에 포함할 수도 있습니다만 , 초기설정이라고 하는 의미로 , 프롭퍼티를 정의한 뒤에 들어갈 수 있으면(자) 좋을 것입니다.

●jar태스크

 jar태스크는 ,jar파일을 작성하는 태스크로 ,겉(표)2의 인수를 가집니다.

표2 jar태스크의 인수
인수기능 필수인가?
basedir jar에 포함하는 파일의 루트 디렉토리YES
destfile jar파일명YES
manifest마니페스트 파일NO

 예를 들면 , 마니페스트 파일에manifest를 지정해build/classes이하의 파일을jar에 압축하고 싶은 경우 , 다음과 같이 기술합니다.

<jar destfile="foo.jar"
basedir="build/classes"
manifest="manifest"/>

●war태스크

 war태스크는 ,Web어플리케이션의WAR어카이브(archive)를 작성합니다. war파일 자체 , 원래jar형식의 파일인 일도 있어 ,jar파일과 같습니다만 ,war용으로 몇개인가 인수가 추가되고 있습니다. 여기에서는 ,jar에 추가된 인수를겉(표)3에 소개합니다 ( 실은 ,war태스크를 실행하는 클래스 자신이jar태스크의 클래스를 계승하고 있습니다 ).


표3 war태스크의 인수
인수기능 필수인가?
webxmlWeb어플리케이션의 배치 기술자META-INF/web.xml파일을 지정YES
lib WEB-INF/lib 디렉토리에 포함하는jar파일. war태스크에 네스트 한 태그로서 포함하는

NO

classes WEB-INF/classes디렉토리에 포함하는 클래스 파일을 지정. war태스크에 네스트 한 태그로서 포함한다NO

※:update옵션(기존의 파일을 덧쓰기한다 ) 을true로 설정해 있으면 , 설정할 필요는 없습니다

 Web어플리케이션의 배치 기술자에게myweb.xml, 서브 렛등의 클래스 파일을classes디렉토리 이하의 파일로 지정해 ,lib디렉토리 이하의jar파일을 포함하는 샘플은 다음과 같이 됩니다.

<war dest="sample.war"
     webxml="myweb.xml">
    <classes dir="classes" />
    <lib dir="lib/*.jar" />
</war>

 classes(와)과lib는 , 네스트 한 태그에 포함할 필요가 있는 점에 주의해 주세요.

●javadoc태스크

 javadoc(을)를 생성하는 태스크로 , 겉(표)4의 인수를 취합니다.

표4 javadoc태스크의 인수
인수기능 필수인가?
(디폴트치)
packagenamesjavadoc(을)를 생성하고 싶은 패키지명YES
lib WEB-INF/lib 디렉토리에 포함하는jar파일. war태스크에 네스트 한 태그로서 포함하는

NO

classes WEB-INF/classes디렉토리에 포함하는 클래스 파일을 지정. war태스크에 네스트 한 태그로서 포함하는NO
sourcepathjava파일의 장소YES
destdir출력하는 디렉토리

YES

Windowtitle브라우저의 바에 표시하는 타이틀을 지정NO
Doctitle개요에 표시하는 타이틀을 지정NO
encoding Java파일의 인코딩NO (시스템의 인코딩)
docencoding 출력하는Javadoc의 인코딩NO (시스템의 인코딩)
excludes컴파일 대상으로부터 제외하고 싶은 파일의 리스트NO
bottomHTML파일의 맨 밑에 삽입하는 텍스트를 지정NO(저작권 정보등을 넣는데 이용)
Author 코드중의@author태그로 지정한 개발자명을 포함하는NO(false)
(true/false를 지정)

 javadoc(은)는 , 다음과 같이 실행합니다.

<javadoc sourcepath="src/java
    excludes="**/bak/*"
    encoding="Windows-31J"
    packagenames="*"
    Windowtitle="my application"
    doctitle="my application"
    docencoding="iso-2022-jp"
    bottom="<center>&copy Arege Building</center>"
    Author="true"
    destdir="api"/>

●echo태스크

 빌드중에 메세지를 표시할 수가 있습니다. echo(은)는 , 다음과 같이 사용합니다.

<echo message="This is echo message" />

 빌드 파일을 디버그 할 때에 프롭퍼티의 값이나 실행 경과를 표시하거나 빌드 방법의 표시 등에 이용하면(자) 편리합니다.

 

 일본어 이용시의 주의점

Ant을 일본어 환경에서 이용할 때에 몇개인가 주의해야 할 점이 있으므로 , 조금 말해 둡니다.

●빌드 파일의 인코딩을 지정하는 것

 빌드 파일의xml선언부에서 , 빌드 파일의 인코딩을 ,

<?xml version="1.0" encoding="Windows-31J"?>

과 같이encoding속성으로 지정할 필요가 있습니다. 이 예에서는 ,Windows-31J(MS932) 를 지정해 있습니다만 ,UNIX등 을 사용의 경우는 ,euc-jp 등에 바꾸어 주세요. 문자 코드가 지정되어 있지 않은 경우 , 빌드 파일 읽기시에 에러가 발생합니다. 덧붙여서 디폴트 인코딩은UTF-8이므로 ,UTF-8그리고 빌드 파일을 기술하면 ,encoding속성을 지정할 필요는 없습니다.

●javac,javadoc태스크의 인코딩을 지정하는 것

 원시 코드에 일본어가 포함되는 경우는 ,javac태스크 중(안)에서 다음과 같이 반드시 원시 코드의 인코딩을 지정하도록 해 주세요.

<javac encoding="Windows-31J"
    …

 이 기술을 실시하지 않아도 , 시스템 디폴트의 인코딩으로 원시 코드를 기술하고 있으면 문제 없게 컴파일 할 수 있습니다만 ,Windows위에서 작성한 코드를Linux위에서 컴파일 하는 등 , 디폴트의 캐릭터 세트가 다른 환경에서Ant를 실행하면(자) , 올바르고 컴파일 할 수 없게 됩니다. encoding(은)는 항상 지정하도록 해 둡시다. javadoc태스크의 경우는 , 다음과 같이 소스의 인코딩 , 출력HTML의 인코딩 , 로케일을 지정하도록(듯이) 합니다 (로케일을 지정하지 않으면 영어 환경에서javadoc를 이용하면(자) ,javadoc가 생성하는HTML이 영어가 될테니 주의해 주세요 ).

<javadoc
    encoding="Windows-31J"
    docencoding="iso-2022-jp"
    locale="ja"
    …

 이 예에서는 , 소스가Windows-31J,javadoc그리고 출력하는HTML파일은iso-2022-jp됩니다. 그리고 , 그 다른 태스크에서도 , 적당 인코딩을 지정하는 버릇을 붙여 두는 것이 좋을 것입니다.

 

COLUMN   파일 세트와 패턴

Ant을 이용할 때에 기억해 두면(자) 편리한 기능에 파일 세트와 패턴이 있습니다. 파일 세트는 그 이름대로 「파일의 모임」을 기술한 것입니다. fileset(은)는 ,java의 소스 ,class파일 등 , 어떠한 파일에서도 취급할 수가 있습니다만 , 특히 클래스 패스를 설정할 때jar파일의 집합을 정의하는데 편리합니다. 예를 들면 , 다음과 같이lib디렉토리아래에 컴파일시에 필요한jar파일이 있다고 합니다.

myproject/
lib/
commons-logging.jar
oro.jar
servlet.jar
ext/j2ee.jar

이 때 ,

<!-- 클래스 패스의 설정 -->
<path id="clspath">
<!-- lib디렉토리 이하의 것include으로 지정한 파일을clspath
에 포함한다. -->
<fileset dir="lib">
<!-- 모든jar파일을 클래스 패스에 포함하는 -->
<include name="*.jar"/>
</fileset>
</path>



<javac src="...">
<!--클래스 패스에clspath를 추가 -->
<classpath refid="clspath" />
</javac>

그렇다면 ,lib이하의 적색의jar파일(commons-logging.jar,oro.jar,servlet.jar) 에 대해서 , 간단하게 클래스 패스에 통할 수가 있습니다. Ant그럼 , 패턴으로 불리는 정규 표현을 간단하게 한 것을fileset태그 중(안)에서 이용할 수가 있어*는 ,0개 이상의 문자를 나타내는 패턴이므로 ,*.jar에 의해 커런트 디렉토리(여기에서는lib) 중의 확장자(extension)가jar인 파일 모든 것을 지정할 수 있게 됩니다. 그리고 ,

<fileset dir="lib">
<include name="**/*.jar">
</fileset>

와**를 이용하면(자) , 커런트 디렉토리 이하의 확장자(extension)가jar인 파일을 재귀적으로 찾아내 , 모든jar을 지정할 수가 있습니다. 상기의 예에서는 ,j2ee.jar을 포함한 적색 , 청색 모든jar파일을 포함할 수가 있습니다.

 다음번은 , 응용편이라고 하는 것으로 ,Ant를 사용해 ,1개의Java코드로부터 간단하게EJB를 작성하는 방법을 소개합니다.

필자 프로필
오카모토 타카시(오카모토 타카시)

오카야마 대학 공학 연구과 수료 후 , (주)NTT데이터에 입사. 캐릭터 레코그네이션 소프트웨어의 연구개발을 거쳐 ,Web서비스 관련의 연구개발에 종사한다. 개인에서는 ,Debian GNU/Linux이 뛰어난 메인트넌스성과 다른 디스트리뷰션을 압도하는 패키지수에 끌리고Debian를 사용하기 시작한 것을 계기로 ,Debian프로젝트의 개발자가 된다. Debian프로젝트의Stefan Gybas,Ola Lundqvist들과 함께 ,Java서포트의 강화를 실시한다. Jakarta에 관해서는 ,Tomcat/JMeter/ORO/Lucene등의 국제화/일본어에의 로컬라이즈 ,Ant의Kaffe VM대응등을 실시하고 있다. 「Jakarta프로젝트 철저 공략」(기술 평론사) , 「WEB+DB PRESS」(기술 평론사) , 「Java World」(IDG재팬) , 「JAVA Developer」(소프트뱅크 퍼블리싱) 등으로 집필 활동을 실시하고 있다.

Ja-Jakarta Project에 붙어
Ja-Jakarta프로젝트에서는 ,Jakarta프로젝트의 문서의 일역이나 프로덕트의 국제화/일본어화등을 실시하고 있다
. 현재 , 프로젝트의 멤버를 모집중. Ja-Jakarta프로젝트의 활동에 참가하자고 하는 (분)편은 ,
「Ja-Jakarta프로젝트에의 참가 방법」 (http://www.ingrid.org/jajakarta/site/getinvolved.html) 을 참조.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
by 꿈꾸는자의 생각의파편들 2005.02.13 17:14
| 1 2 3 4 |

티스토리 툴바