반응형

DbUtils 몇가지 예제


DBUtils 기본은 다음 링크를 참조하세요

http://www.jakartaproject.com/article/jakarta/1108193481660


설정방법

   DB유틸 설정 방법은 특별히 없습니다  그냥 다운받은 클래스 패스 잡으시면 됩니다

   Application에서 사용시에는 환경변수나 실행시 클래스 패스를 잡으면 되고요,

   웹에서 사용한다면 해당 어플리케이션의 /WEB-INF/lib/ 에 commons-beanutils.jar 를 복사하면 됩니다


   기본적인 문서는 http://www.jakartaproject.com/article/jakarta/1108193481660 를 보세요


   Download http://jakarta.apache.org/site/downloads/downloads_commons-dbutils.cgi

   API http://jakarta.apache.org/commons/dbutils/apidocs/index.html


SELECT 예제 (여러건)


<%@ page contentType="text/html;charset=EUC_KR" %>
<%@ page import="com.jakartaproject.board.vo.*,org.apache.commons.dbutils.*,java.sql.*,java.util.*, org.apache.commons.dbutils.handlers.*" %>

<%
       Connection conn = null;

       try {

           DbUtils.loadDriver("com.mysql.jdbc.Driver");

           conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "test", "1111");


           ArrayList params = new ArrayList();
           params.add("1%");


           ResultSetHandler rsh = new BeanListHandler(BoardVO.class);

           QueryRunner qr = new QueryRunner();

           List list = (List)qr.query(conn, "SELECT boardTitle, boardContent, userNick FROM board_test_t WHERE userIp like ?", params.toArray(), rsh);


           for (int i = 0; i < list.size(); i++) {

               BoardVO board = (BoardVO)list.get(i);

               System.out.println(board.getBoardTitle());

               System.out.println(board.getBoardContent());

               System.out.println(board.getUserNick());

          }

       } catch (Exception e) {

           System.out.println(e);

       } finally {

           DbUtils.closeQuietly(conn);

       }
%>


SELECT 예제 (한건)

select 처리 건수가 1건일 경우에는 MapHandler를 사용하면 됩니다

<%@ page contentType="text/html;charset=EUC_KR" %>
<%@ page import="org.apache.commons.dbutils.*,java.sql.*,java.util.*, org.apache.commons.dbutils.handlers.*" %>

<%
       Connection conn = null;

       try {

           DbUtils.loadDriver("com.mysql.jdbc.Driver");

           conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "test", "1111");


           ResultSetHandler rsh = new MapHandler();

           QueryRunner qr = new QueryRunner();

           Map map = (Map)qr.query(conn, "SELECT count(*) cnt FROM board_test_t", rsh);

           System.out.println(map.get("cnt"));

           

       } catch (Exception e) {

           System.out.println(e);

       } finally {

           DbUtils.closeQuietly(conn);

       }
%>

핸들러에는 아래와 같이 여러 종류의 핸들러 들이 있으며,

ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, ColumnListHandler, KeyedHandler, MapHandler, MapListHandler, ScalarHandler

그때그때 맞춰 사용하면 됩니다



UPDATE 예제

이 예제는 Unicorn 소스에 있는 예입니다

Unicorn 소스를 다운받아 /src/com/jakartaproject/admin/dao/AdminMySqlDAO.java 를 열어 보시면 Update, Insert 예제를 볼수 있습니다


public void setBoardCommonSecurity(ConnectionContext connectioncontext,

                                                  AdminForm adminForm) throws BaseException {


        String updateQuery = "UPDATE board_common_t SET badIp=?, badId=?, badNick=?, badContent=?, inputPerMin=?, tryLogin=?";

        try {
            ArrayList params = new ArrayList();
            params.add(encode(adminForm.getBadIp()));
            params.add(encode(adminForm.getBadId()));
            params.add(encode(adminForm.getBadNick()));
            params.add(encode(adminForm.getBadContent()));
            params.add(String.valueOf(adminForm.getInputPerMin()));
            params.add(String.valueOf(adminForm.getTryLogin()));

            QueryRunner queryRunner = new QueryRunner();
            queryRunner.update(connectioncontext.getConnection(), encode(updateQuery), params.toArray());

        } catch (Exception e) {
            logger.error("Error at AdminDAO.setBoardCommonSecurity",e);
            BaseException baseException = new BaseException("errors.sql.problem");
            throw baseException;
        }

        logger.info("AdminDAO.setBoardCommonSecurity was executed");
    }

Posted by 1010
반응형

Jakarta Commons Net 에서 FTP 사용시 목록이 안보일 경우



Jakarta Commons Net 기본 http://www.jakartaproject.com/article/jakarta/1113911351166

Commons net API http://jakarta.apache.org/commons/net/apidocs/index.html

Commons net http://jakarta.apache.org/commons/net/


조회 권한이 없는 경우

   로그인한 유저의 권한이 적당한지 체크해 본다

   또는 텔넷을 통해 해당 유저로 ftp 테스트를 해 본다


Passive mode를 사용해 본다

   FTPClient ftpClient = new FTPClient();

   ftpClient.connect(server);

   ...

   ftpClient.login(username, password);

   ftpClient.enterLocalPassiveMode();

   ...


관련 함수
   enterRemoteActiveMode(InetAddress host, int port)
 
 
날짜 포맷을 변경한다
현재 서버의 날짜가 한글로 나오도록 설정 되었다면 목록이 안나올 수가 있습니다
 
FTPClient ftpClient = new FTPClient();
FTPClientConfig config = new FTPClientConfig(FTPClientConfig.SYST_NT); 
config.setServerLanguageCode("ko");
config.setDefaultDateFormat("d MMM yyyy");
config.setRecentDateFormat("d MMM HH:mm");
ftpClient.configure(config);
ftpClient.connect(server);
...
 
운영체제에 맞게 설정하세요
  FTPClientConfig.SYST_NT
  FTPClientConfig.SYST_MVS
  FTPClientConfig.SYST_OS2
  FTPClientConfig.SYST_OS400
  FTPClientConfig.SYST_UNIX
  FTPClientConfig.SYST_VMS
 
언어설정값은 http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt 에서 확인하세요
날짜 포맷은 java.text.SimpleDateFormat 의 형식을 따릅니다
 
또다른 방법이 있으면 올려주세요 ^^;
Posted by 1010
반응형

from http://www.javaservice.net/~java/bbs/read.cgi?m=resource&b=qna2&c=r_p&n=1131692449&p=1&s=t

 

제목 : POI로 엑셀파일 읽을때, Invalid header signature 에러
글쓴이: 손님(guest) 2005/11/11 11:37:30 조회수:20 줄수:44
poi를 이용하여 클라이언트의 엑셀문서를 서버에 업로드후 읽어내는 작업을 하려합니다.
작업의 목적은 화면상에 입력필드가 너무 많아서(대략 240여개 정도 됩니다),
일일이 입력하기에 불편함이 초래되어,엑셀로 다운받은 다음에 해당 입력값들을 엑셀에서 입력후,
입력한 값을 다시 화면상에 뿌려주려는 것입니다.

업로드 완료후 엑셀파일을 읽을때,

Error occurred : Invalid header signature; read 5789751444030890300, expected -2226271756974174256
java.io.IOException: Invalid header signature; read 5789751444030890300, expected -2226271756974174256
        at org.apache.poi.poifs.storage.HeaderBlockReader.<init>(HeaderBlockReader.java:88)
        at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:83).......

 

위와 같은 에러가 발생합니다.해당 라인을 쫓아가보면,

POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(filePath + fileName));

이부분입니다.

읽으려 하는 엑셀은 jsp내에서,

response.setContentType("application/vnd.ms-excel;charset=euc-kr"); 
response.setHeader("Content-Disposition", "attachment; filename="+jspName+".xls"); 
response.setHeader("Content-Description", "JSP Generated Data"); 

을 이용해서 생성한것입니다.해당 엑셀파일을 일반 텍스트 에디터에서 열어보면,

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns="http://www.w3.org/TR/REC-html40">

<head>
<meta http-equiv=Content-Type content="text/html; charset=ks_c_5601-1987">
<meta name=ProgId content=Excel.Sheet>
(이하 생략)

meta 태그내의 'content="text/html;' 부분이 의심이 갑니다만.
엑셀을 생성하는 jsp내의 meta 태그부분을 삭제해도,생성된 엑셀을 보면 해당부분이 들어가있는데요.

다시 처음으로 와서,content='text/html' 부분때문에 Invalid header signature...의 오류가 발생하는것인지요?
jsp에서 생성된 엑셀이 아닌 일반 엑셀문서로 테스트 해봤을때는 이상없이 제대로 실행됩니다.
제가 의심한 부분이 맞는것이라면, 해결책은 어떤것이 있을런지 조언 부탁드립니다.
제목 : Re: 실제 엑셀파일이 아니라서 그런거 같습니다.
글쓴이: 손님(guest) 2005/11/11 16:00:49 조회수:7 줄수:16
 Invalid Header라는 에러메시지의 의미가

 파일 헤더가 엑셀타입이 아니라서 그런거 같습니다.

 POI가 해석하는 엑셀파일은 MS OLE Compound로 생성된 파일포맷일 경우에만

 해당됩니다..html의 header를 excel이라 조작해서 생성된 엑셀파일은

 실제론 텍스트파일일뿐 OLE 타입이 아니라서요.

 서버에서 생성시 jsp로 생성하지 말고 poi를 이용해서 서버측에 실제

 엑셀파일을 생성한 후 클라이언트로 스트리밍을 통해 다운로드 해주는 방식을

 택해야할 것같네요.
제목 : Re: 답변 고맙습니다.
글쓴이: 손님(guest) 2005/11/11 16:31:58 조회수:2 줄수:5
그렇군요.
실제로 jsp내에서 생성되는 엑셀을 다른이름으로 저장해서 'Micrisoft Excel 통합문서' 형식으로 저장하니,
정상적으로 실행이 되는군요.
충고해주신데로, poi를 이용해서 엑셀을 생성하는 쪽으로 다시 해봐야겠습니다.
답변 고맙습니다.좋은 주말 되세요.

Posted by 1010
반응형

JSP에서 원하는 Appender 선택하여 쓰기


만약 log4j 가 처음이라면 다음 링크를 먼저 필독하세요


log4j 웹에서 사용하기

http://www.jakartaproject.com/article/jakarta/1110438405982

log4j 고급스럽게 사용하기

http://www.jakartaproject.com/article/jakarta/1113816023183


 

I. 먼저 log4j 프로퍼티 파일입니다

log4j.properties

log4j.rootLogger=INFO, stdout1, stdout2


log4j.logger.jsp1=INFO, stdout1
log4j.additivity.jsp1=false


log4j.logger.jsp2=INFO, stdout2
log4j.additivity.jsp2=false


log4j.appender.stdout1=org.apache.log4j.ConsoleAppender
log4j.appender.stdout1.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout1.layout.ConversionPattern=jsp1 appender log %d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


log4j.appender.stdout2=org.apache.log4j.ConsoleAppender
log4j.appender.stdout2.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout2.layout.ConversionPattern=jsp2 appender log %d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


log4j.logger.jsp1=INFO, stdout1
log4j.logger.jsp2=INFO, stdout2
jsp1과 jsp2의 두개의 logger를 정의합니다

jsp1 logger는 appender로 stdout1을 사용하며, jsp2 logger는 appender로 stdout2로 사용한다는 의미입니다


log4j.additivity.jsp1=false

additivity 속성은 jsp1 logger를 상위 로거(root logger)의 속성을 삭송받지 않겠다는 의미입니다

만약 이 속성이 없으면 동일한 메세지가 여러번 로깅될 것입니다

이하 속성은

http://www.jakartaproject.com/article/jakarta/1110438405982 을 참고하세요


II. JSP 샘플 소스

test_jsp1_appender.jsp

<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.Logger" %>

<%!
 static Logger logger1 = Logger.getLogger("jsp1");
%>

<%
logger1.warn("warn");
%>

로깅 메세지

jsp1 appender log 2005-11-07 13:05:23,687 WARN  [http-8080-Processor5] jsp1 (test_jsp1_appender_jsp.java:48)     - warn


test_jsp2_appender.jsp

<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.Logger" %>

<%!
 static Logger logger2 = Logger.getLogger("jsp2");
%>

<%
logger2.warn("warn");
%>

로깅 메세지

jsp2 appender log 2005-11-07 13:05:58,031 WARN  [http-8080-Processor4] jsp2 (test_jsp2_appender_jsp.java:48)     - warn


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Jakarta Commons Email


I. Commons-Email

2005년 9월 27일 드디어 Commons-Email이 version 1.0이 배포되었습니다

Commons-Email은 Java Mail API를 근간으로 좀더 심플하게 메일을 보내는 방안을 제시합니다.

기능상으로는 첨부파일 뿐만 아니라 HTML Email도 쉽게 보낼수 있는 API를 제공합니다


II. Download!

Commons-Email을 사용하기 위해서는 JavaMail의 mail.jar와 JAF의 activation.jar 가 필요합니다

Commons-Email download

http://jakarta.apache.org/site/downloads/downloads_commons-email.cgi

JavaMail 1.3

http://java.sun.com/products/javamail/downloads/index.html

JAF 1.0.2

http://java.sun.com/products/javabeans/glasgow/jaf.html


Commons-Email

http://jakarta.apache.org/commons/email/index.html

Commons-Email API

http://jakarta.apache.org/commons/email/apidocs/index.html



III. Install




다운 받은 3개의 파일을 $tomcat_home/webapps/ROOT/WEB-INF/lib/ 밑에 복사합니다



IV. 샘플 소스


간단히 텍스트만 보내기

SimpleEmail email = new SimpleEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("Test message");
email.setMsg("This is a simple test of commons-email");
email.send();

org.apache.commons.mail.SimpleEmail 은 가장 중심이 되는 org.apache.commons.mail.Email을 상속받아 setMsg(java.lang.String msg)만을 구현한 가장 기본적인 클래스입니다.

setHostName(java.lang.String aHostName)으로 SMTP 서버를 지정하고,

addTo(java.lang.String email) or addTo(java.lang.String email, java.lang.String name)으로 받는 사람의 메일주소를, setFrom(java.lang.String email) or setFrom(java.lang.String email, java.lang.String name)으로 보내는 사람의 메일을 설정합니다

물론 addTo 함수의 추가로 여러 사람에게 메일을 보낼 수 있습니다.

가장 기본적인 setSubject(java.lang.String subject)와 setMsg(java.lang.String msg)로 메일 제목과 내용을 입력한 후 send() 함수로 전송합니다


파일 첨부하기

// 첨부할 attachment 정보를 생성합니다
EmailAttachment attachment = new EmailAttachment();
attachment.setPath("mypictures/john.jpg");
attachment.setDisposition(EmailAttachment.ATTACHMENT);
attachment.setDescription("Picture of John");
attachment.setName("John");

// 기본 메일 정보를 생성합니다

MultiPartEmail email = new MultiPartEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("The picture");
email.setMsg("Here is the picture you wanted");

// 생성한 attachment를 추가합니다
email.attach(attachment);

// 메일을 전송합니다
email.send();

첨부파일과 같이 보낼려면 EmailAttachment 를 생성하여 파일 정보를 입력해 줍니다

파일경로와 파일형태, 파일 설명등을 추가하며 마지막으로 setName(java.lang.String name)을 통해 첨부되는 파일명을 설정합니다

그 후 MultiPartEmail 을 통해 SimpleEmail 처럼 기본 메일정보를 설정합니다

마지막으로 MultiPartEmail의 attach() 함수를 통해 첨부 파일을 추가하여 전송합니다

만약 첨부파일이 두개 이상이라면 EmailAttachment 를 여러개 생성하여 파일 정보를 설정 한 후 attach()를 통해 추가해 주기만 하면 됩니다


URL을 통해 첨부하기

// 첨부할 URL정보 및 파일 기본 정보를 설정합니다
EmailAttachment attachment = new EmailAttachment();
attachment.setURL(new URL("http://www.apache.org/images/asf_logo_wide.gif"));
attachment.setDisposition(EmailAttachment.ATTACHMENT);
attachment.setDescription("Apache logo");
attachment.setName("Apache logo");

// 기본 메일 정보를 생성합니다
MultiPartEmail email = new MultiPartEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("The logo");
email.setMsg("Here is Apache's logo");

// attachment를 추가합니다
email.attach(attachment);

// 메일을 전송합니다
email.send();

파일 경로 정보를 setURL(java.net.URL) 으로 설정할 뿐 위의 첨부파일과 동일합니다


HTML 이메일 보내기

// 기본 메일 정보를 생성합니다
HtmlEmail email = new HtmlEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("Test email with inline image");

// 삽입할 이미지와 그 Content Id를 설정합니다
URL url = new URL("http://www.apache.org/images/asf_logo_wide.gif");
String cid = email.embed(url, "Apache logo");

// HTML 메세지를 설정합니다

email.setHtmlMsg("<html>The apache logo - <img src=\"cid:"+cid+"\"></html>");

// HTML 이메일을 지원하지 않는 클라이언트라면 다음 메세지를 뿌려웁니다
email.setTextMsg("Your email client does not support HTML messages");

// 메일을 전송합니다
email.send();

HtmlEmail 클래스는 setHtmlMsg()로 작성된 html을 보낼 수 있습니다

이미지 처리가 조금 까탈스럽네요



V. 인증처리

만약 SMTP 서버가 인증을 요구한다면 org.apache.commons.mail.Email 의 setAuthentication(java.lang.String username, java.lang.String password)를 통해 해결할 수 있습니다

이 함수는 JavaMail API의 DefaultAuthenticator 클래스를 생성하여 사용합니다



=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Jakarta POI

 


X. 이미지


POI 3.0 부터 드디어 이미지를 지원하는군요.

아직 알파버젼이구요 http://www.apache.org/dyn/closer.cgi/jakarta/poi/ 에서 최신버젼을 다운 받을 수 있습니다


최신링크

http://mirror.apache.or.kr/jakarta/poi/dev/bin/poi-bin-3.0-alpha1-20050704.zip


아래 소스는 OKJSP의 "이루"님이 작성한 소스입니다

from http://www.okjsp.pe.kr/bbs?act=VIEW&seq=60543&bbs=bbs4&keyfield=content&keyword=&pg=0


import java.util.*;
import java.io.*;

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem.*;
import org.apache.poi.poifs.eventfilesystem.*;


public class InsertImage {
    public static void main(String[] args) throws IOException {
        POIFSFileSystem fs      = new POIFSFileSystem(new FileInputStream("/xxx/test.xls")); // 원본엑셀파일
        HSSFWorkbook    wb      = new HSSFWorkbook(fs);
        HSSFSheet       sheet   = wb.getSheetAt(0);

        drawSheet5( sheet, wb );

        // Write the file out.
        FileOutputStream fileOut = new FileOutputStream("/xxx/img_work.xls"); // 이미지 삽입된 엑셀파일
        wb.write(fileOut);
        fileOut.close();
    }


    private static void drawSheet5( HSSFSheet sheet, HSSFWorkbook wb ) throws IOException {
        // Create the drawing patriarch.  This is the top level container for
        // all shapes. This will clear out any existing shapes for that sheet.
        HSSFPatriarch patriarch = sheet.createDrawingPatriarch();

        HSSFClientAnchor anchor;
        anchor = new HSSFClientAnchor(0,0,0,255,(short)1,4,(short)2,4); // 이미지 크기조절은 여기서..
        anchor.setAnchorType( 2 );
        patriarch.createPicture(anchor, loadPicture( "/xxx/okjsp.jpg", wb )); // 삽입 할 이미지
    }


    private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException {
        int pictureIndex;
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;

        try {
            fis = new FileInputStream( path);
            bos = new ByteArrayOutputStream( );
            int c;
            while ( (c = fis.read()) != -1) {
                bos.write( c );
            }
            pictureIndex = wb.addPicture( bos.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG  );
        } finally {
            if (fis != null) fis.close();
            if (bos != null) bos.close();
        }
        return pictureIndex;
    }
}


Posted by 1010
반응형

Jakarta Ant


-. 이번 강좌에서는 지난 시간에 이어 ant 사용에 필수적인 task들에 대해 알아보고

   예제를 통해 한번더 알아보도록 하겠습니다.

   만약 지난 강좌를 아직 보지 않았다면 다음 링크를 참조하세요

   http://www.jakartaproject.com/article/jakarta/1114617006525



I. 집합 태그


1. <fileset>

개요

-. fileset이란 말 그대로 파일의 집합을 말하며 특정 태스크에서 파일을 포함하여야 할때

손쉽게 특정 파일이나 집합을 지정해 주는데 사용된다

주로 클래스패스나, 압축, 파일 카피등의 task에서 사용되어진다


예제

사용예제-1


<fileset dir="${server.src}" casesensitive="yes">
  <include name="**/*.java"/>
  <exclude name="**/*Test*"/>
</fileset>
<fileset dir="${server.src}" casesensitive="yes">
  <filename name="**/*.java"/>
  <filename name="**/*Test*" negate="true"/>
</fileset>
<fileset dir="${server.src}" casesensitive="yes"> <filename name="**/*.java"/> <not> <filename name="**/*Test*"/> </not> </fileset>

모든 디렉토리의 java파일을 포함시키면서 파일이름중 Test가 들어가는 파일은 제외하는 fileset을 나타낸다

위 세가지 fileset은 같은 의미이다


사용예제-2


<fileset dir="${server.src}" casesensitive="yes">
  <include name="**/*.java, **/*.properties"/>
</fileset>
<fileset dir="${server.src}" casesensitive="yes">
  <include name="**/*.java"/>
<include name="**/*.properties"/>
</fileset>

","를 사용하여 나타낼 수도 있고 다로 분리하여 tag를 사용할 수도 있다

위 두 fileset은 같은 의미이다


속성

속성 설명 필수여부
dir 파일집합의 최상위 디렉토리 (루트 디렉토리) dir나 file둘중 하나는 필수
file 특정 하나의 파일을 지정할때 사용
defaultexcludes default excludes 에 명시된 파일 및 디렉토리를 자동적으로 제외하는 속성이며 이기능을 사용하려면 생략한다. 혹은 yes/no 로 값을 준다 No
includes 컴마나 스페이스로 구분된 파일의 패턴을 포함하는 속성이며 모든 파일을 사용하려면 생략한다 No
includesfile 특정 각각의 파일을 포함할때 사용하는 속성 No
excludes 컴마나 스페이스로 구분된 파일의 패턴을 제외하는 속성이며 생략하면 default excludes를 제외하고 어떠한 파일도 제외하지 않는다 No
excludesfile 특정 각각의 파일을 제외할때 사용하는 속성 No
casesensitive 패턴의 대소문자를 구분할지 여부를 나타내는 속성으로 기본값은 true이다 No
followsymlinks symbolic links를 허용할지 여부를 나타내며 기본값은 true이다 No


default excludes

excludes를 사용하지 않아도 기본적으로 제외되는 파일 및 디렉토리

     **/*~
     **/#*#
     **/.#*
     **/%*%
     **/._*
     **/CVS
     **/CVS/**
     **/.cvsignore
     **/SCCS
     **/SCCS/**
     **/vssver.scc
     **/.svn
     **/.svn/**
     **/.DS_Store

자 이정도면 fileset은 이제 빠~삭해 지셨겠죠?



2. <dirset>

개요

-. fileset이 파일 집합을 나타냈다면 dirset task는 디렉토리의 집합을 나타낸다. 기본적인 속성은 fileset과 동일하다

차이점은 defaultexcludes가 없다


사용예제


<dirset dir="${build.dir}"> <include name="apps/**/classes"/> <exclude name="apps/**/*Test*"/> </dirset>

apps 이하 디렉토리중 classes 디렉토리를 포함하고 apps 이하 디렉토리중 디렉토리 이름중 Test가 들어가는 디렉토리는 제외한 dirset을 의미한다


속성

속성 설명 필수여부
dir 파일집합의 최상위 디렉토리 (루트 디렉토리) YES
includes 컴마나 스페이스로 구분된 디렉토리의 패턴을 포함하는 속성이며 모든 파일을 사용하려면 생략한다 No
includesfile 특정 각각의 디렉토리를 포함할때 사용하는 속성 No
excludes 컴마나 스페이스로 구분된 디렉토리의 패턴을 제외하는 속성이며 생략하면 어떠한 디렉토리도 제외하지 않는다 No
excludesfile 특정 각각의 디렉토리를 제외할때 사용하는 속성 No
casesensitive 패턴의 대소문자를 구분할지 여부를 나타내는 속성으로 기본값은 true이다 No
followsymlinks symbolic links를 허용할지 여부를 나타내며 기본값은 true이다 No



3. <patternset>

개요

-. 여러 파일이나 디렉토리를 미리 그룹핑해 놓고 이를 특정 아이디를 통해 사용하는 방식을 제공한다. 즉 워드의 매크로 같은 기능이라 보면 쉽게 이해가 갑니다


사용예제

<patternset id="non.test.sources">
  <include name="**/*.java"/>
  <exclude name="**/*Test*"/>
</patternset>
...
<fileset dir="${server.src}">
<patternset refid="non.test.sources"/>
</fileset>
...
<fileset dir="${server.src}">
<patternset refid="non.test.sources">
<include name="**/*.java"/>
</patternset>
</fileset>

예제를 보는바와 같이 patternset은 여러곳에서 동일한 패턴을 사용할때 이를 미리 정의해 놓고 지정한 id의 값을 refid를 통해 재사용 할 수 있다

paternset을 사용하면 코딩양을 줄일 수 있고 변동사항이 있으면 손쉽게 변동 할 수 있다는 점이다


속성

속성 설명 필수여부
includes 컴마나 스페이스로 구분된 디렉토리의 패턴을 포함하는 속성이며 모든 파일을 사용하려면 생략한다 No
includesfile 특정 각각의 디렉토리를 포함할때 사용하는 속성 No
excludes 컴마나 스페이스로 구분된 디렉토리의 패턴을 제외하는 속성이며 생략하면 어떠한 디렉토리도 제외하지 않는다 No
excludesfile 특정 각각의 디렉토리를 제외할때 사용하는 속성 No



4. <path>

개요

-. 파일과 디렉토리를 함께 나타낼 수 있는 집합. 클래스패스에는 경로뿐만 아니라 *.jar 같은 파일이 같이 사용되듯이 이 path task는 주로 클래스패스로 자주 이용된다

fileset, dirset과 비슷하지만 파일과 디렉토리를 함께 나타낼 수 있다는 점에서 틀리다

path tag역시 patternset과 같이 지정된 id 값을 이용하여 여러 곳에서 재사용 할 수 있다.


사용예제

아래 예제는 ${basedir}/lib/struts.jar, ${base.dir}/lib/log4j.jar 그리고 ${base.dir}/classes 에 클래스패스를 잡는 예제이다.

<path id="base.path">
<pathelement path="lib/struts.jar;lib/log4j.jar"/> <pathelement location="classes"/> </path> ...
<classpath refid="base.path"/>
...

path에 대한 정의를 해놓고 base.path라는 아이디를 이용하여 여러 곳에서 사용할 수 있다


location 속성은 single 파일이나 탐색할 하나의 디렉토리만을 설정할수 있는 반면에,

path는 ";"나 ":"를 사용하여 여러 파일들이나 디렉토리를 설정할 수 있다


II. 파일 태그


1. <copy>

개요

-. 소스 파일을 타겟으로 파일을 복사하는데 사용한다


사용예제

파일 하나 복사

<copy file="myfile.txt" tofile="mycopy.txt"/>


파일 하나를 디렉토리에 복사

<copy file="myfile.txt" todir="../some/other/dir"/>

 

디렉토리를 다른 디렉토리로 복사 

<copy todir="../new/dir">
    <fileset dir="src_dir"/>
</copy>

 

파일 집합을 디렉토리에 복사

<copy todir="../dest/dir">
    <fileset dir="src_dir">
      <exclude name="**/*.java"/>
    </fileset>
</copy>

<copy todir="../dest/dir">
    <fileset dir="src_dir" excludes="**/*.java"/>
</copy>

 

파일 집합을 디렉토리에 복사하며 파일명 끝에 .bak을 추가하여 복사

<copy todir="../backup/dir">
    <fileset dir="src_dir"/>
    <globmapper from="*" to="*.bak"/>
</copy>

 

파일집합을 디렉토리에 복사하며 파일집합중 TITLE 이란 STRING을 Foo Bar로 대체하며 복사

<copy todir="../backup/dir">
    <fileset dir="src_dir"/>
    <filterset>
      <filter token="TITLE" value="Foo Bar"/>
    </filterset>
</copy>


속성

속성 설명 필수여부
file 복사할 파일 fileset tag를 사용하지 않는다면 필수
preservelastmodified

소스와 타겟의 최종 수정날짜 파일을 유지하며 복사한다

기본값은 false

No
tofile 복사할 소스파일 fileset tag와 함께 소스 및 타겟을 지정할수있다
todir 복사할 소스 디렉토리
overwrite 타겟의 파일이 최근의 파일이라도 덮어쓸지 여부를 나타내며 기본값은 false No
filtering

복사하는동안 filter를 사용할것인지 여부를 나타내며 기본값은 false

단 이 값이 false일지라도 nested 하게 <filterset>을 사용할수 있다

No
flatten

소스 디렉토리의 구조를 무시하고 특정 디렉토리 한곳에 모든 파일을 복사할지 여부를 나타내며 기본값은 false

No
includeEmptyDirs

<fileset>을 지정하여 복사될 디렉토리중에 파일이 없는 비어있는 디렉토리까지 복사할지 여부를 나타내며 기본값을 true

No
failonerror

복사중 에러가 발생할때 false이면 warning 메세지 로그를, 그렇지 않으면 build시 중지한다

기본값은 true

No
verbose 복사되고 있는 파일의 로그 여부를 나타내며 기본값은 false No
encoding

필터 복사중 사용할 encoding을 나타내며 기본값은 JVM 기본 encoding을 따른다 (ant 1.5 이후)

No
outputencoding

파일기록에 사용할 encoding을 나타낸다

(ant 1.6이후)

No
enablemultiplemappings

true면 주어진 소스 path에대해 모든 mapping 처리를 하며 false이면 첫번째 파일이나 디렉토리에 대해서만 mapping 처리를 한다

이 속성은 <mapper> subelement가 있을때만 관련있다

기본값은 false

(ant 1.6이후)

No
granularity

최근 수정된 시간을 기준으로 파일을 비교 복사시 기준이 되는 시간으로부터 여유분 시간을 나타낸다

즉 소스 파일 서버와 타겟 파일 서버가 다른경우 두 머신간에 시간차이가 있을 수 있기 때문에 이를 사용한다

(ant 1.6.2 이후)

 


2. <delete>

개요

-. 하나의 파일, 하나의 디렉토리 혹은 fileset으로 지정한 특정한 파일들을 삭제할수 있다


사용예제

하나의 파일을 삭제
<delete file="/lib/ant.jar"/>


하나의 디렉토리를 삭제 (이하 디렉토리 및 파일 모두 삭제됨)
<delete dir="lib"/>


현재 디렉토리로 부터 모든 파일중 .bak 파일은 모두 삭제
<delete>
    <fileset dir="." includes="**/*.bak"/>
</delete>


하나의 디렉토리를 삭제하며 빈 디렉토리라도 모두 삭제 (build 자신의 디렉토리도 삭제)
<delete includeEmptyDirs="true">
    <fileset dir="build"/>
</delete>


build 이하 모든 파일을 삭제하며 빈 디렉토리라도 모두 삭제 (build 자신의 디렉토리는 삭제되지 않음)
<delete includeemptydirs="true">
    <fileset dir="build" includes="**/*"/>
</delete>


 


속성

속성 설명 필수사항
file

삭제할 파일

(상대경로나 절대경로나 상관없음)

<fileset>을 사용하지 않는다면 file혹은 dir둘중 하나는 사용하여야 한다
dir

삭제할 디렉토리

verbose

각 삭제되는 파일의 이름을 보져줄 것인지 여부를 나타내며 기본값은 false

No
quiet

시스템이 사용하고 있어 삭제할수 없는 파일이거나

존재하지 않는 파일, 디렉토리등의 메세지를 나태낼지 여부를 나타내며 기본값은 false

No
failonerror

파일 삭제시 에러가 발생하면 build를 중단 하고 현재 사항을 레포팅해줄지 여부를 나타내며 기본값은 true

No
includeemptydirs fileset을 사용할 때 빈 디렉토리empty directory)를 삭제할 것인지 여부를 나타내며 기본값은 false No
includes Deprecated. Use <fileset>. No
includesfile Deprecated. Use <fileset>. No
excludes Deprecated. Use <fileset>. No
excludesfile Deprecated. Use <fileset>. No
defaultexcludes

Deprecated. Use <fileset>.

기본값은 true

No
deleteonexit

파일 삭제시 실패했을때 JVM이 종료될 때 이 파일을 지우도록 할것인지 여부를 나타내며 기본값은 false

(ant 1.6.2 이후)

No


3. <mkdir>

설명

-. 디렉토리를 생성


사용예제

"${dist}" 디렉토리를 생성한다

<mkdir dir="${dist}"/>


"${dist}/lib" 디렉토리를 생성한다

<mkdir dir="${dist}/lib"/>


속성

속성 설명 필수여부
dir 생성할 디렉토리 Yes


4. <move>

설명

-. 파일 혹은 디렉토리를 타겟으로 이동시킵니다 디폴트로 타겟에 이미 파일이 존재하면 덮어씁니다.


사용예제

하나의 파일을 이동시킵니다 (rename과 동일)
<move file="file.orig" tofile="file.moved"/>


하나의 파일을 디렉토리로 이동시킵니다
<move file="file.orig" todir="dir/to/move/to"/>


디렉토리를 새로운 디렉토리로 이동시킵니다
<move todir="new/dir/to/move/to">
    <fileset dir="src/dir"/>
</move>


위의 task와 동일하며 ant 1.6.3부터 지원합니다
<move file="src/dir" tofile="new/dir/to/move/to"/>


파일집합을 새로운 디렉토리로 이동시킵니다
<move todir="some/new/dir">
    <fileset dir="my/src/dir">
      <include name="**/*.jar"/>
      <exclude name="**/ant.jar"/>
    </fileset>
</move>


.bak을 제외한 모든 파일에 .bak을 추가시키며 이동시킵니다
<move todir="my/src/dir" includeemptydirs="false">
    <fileset dir="my/src/dir">
      <exclude name="**/*.bak"/>
    </fileset>
    <mapper type="glob" from="*" to="*.bak"/>
</move>



속성

-. delete 속성과 동일합니다



III. java 태그


1. <javac>

설명

-. 자바소스를 컴파일 하는 tag입니다

기본적으로 타겟에 특정 .java에 대해 .class 파일이 없거나 오래된 .class 파일이면 컴파일 대상입니다


사용예제

<path id="compile.classpath">

    <pathelement location="${catalina.home}/common/classes"/>
    <fileset dir="${web.home}/WEB-INF/lib">
      <include name="*.jar"/>
    </fileset>
</path>


가장 일반직인 컴파일 태그 compile.classpath 는 앞의 path 태그에서 정의됨

<javac srcdir="${src}"

          destdir="${build}">

     <classpath refid="compile.classpath"/>

</javac>


include, exclude, debug등을 포함한 컴파일 태그

<javac srcdir="${src}"
          destdir="${build}"
          includes="mypackage/p1/**,mypackage/p2/**"
          excludes="mypackage/p1/testpackage/**"
          classpath="xyz.jar"
          debug="on"
/>


srcdir 대신 <scr> 태그를 이용한 컴파일 태그

<javac destdir="${build}"
         classpath="xyz.jar"
         debug="on">
    <src path="${src}"/>
    <src path="${src2}"/>
    <include name="mypackage/p1/**"/>
    <include name="mypackage/p2/**"/>
    <exclude name="mypackage/p1/testpackage/**"/>
</javac>



속성

width=20%>속성 설명 필수여부
srcdir 자바 소스가 위치한 디렉토리

Yes

<src>가 존재하면NO

destdir 컴파일된 클래스파일이 위치할 디렉토리 No
includes 컴마나 스페이스로 구분된 파일집합의 패턴을 포함하여 컴파일하며 모든 파일을 사용하려면 생략한다 No
includesfile 각각 개개의 파일을 포함하여 컴파일 No
excludes 컴마나 스페이스로 구분된 파일의 패턴을 제외하하여 컴파일하며 생략하면 어떠한 디렉토리도 제외하지 않는다 No
excludesfile 각각 개개의 파일을 제외하여 컴파일 No
classpath 사용할 클래스 패스 No
sourcepath

사용할 소스패스이며 기본값은 srcdir 값이다

No
bootclasspath bootstrap 클래스 패스 No
classpathref 참조를 통해 사용할 클래스 패스 (refid를 이용한 path 태그) No
sourcepathref 참조를 통해 사용할 소스 패스 No
bootclasspathref 참조를 통해 사용할 bootstrap 패스 No
extdirs 사용할 extednsion이 위치한 경로 (${java_home}/lib/ext/를 말하는것 같음) No
encoding 소스파일에 사용할 encoding (javac의 -encoding과 동일) No
nowarn

컴파일시 -nowarn 옵션을 을 줄지 여부를 나타내며 기본값은 off

반대값은 on

No
debug 컴파일시 debug 모드로 컴파일 하며 기본값은 off No
optimize 컴파일시 optimize 옵션을 사용한다 기본값은 off No
deprecation 컴파일시 deprecation 옵션을 사용한다 기본값은 off No
target 특정 VM 버젼으로 컴파일 할수 있도록 한다 No
verbose

컴파일시 그 내용을 화면에 출력한다

기본값은 no

No
depend

이를 지원하는 컴파일러는 dependecy-tracking를 가능하게 한다

No
includeAntRuntime

ant run time library를 포함하여 컴파일하며 기본값은 yes

No
includeJavaRuntime

VM이 실행될때의 run time library를 포함하여 컴파일하며 기본값은 no

No
fork 외부의 JDK를 사용하여 컴파일 하며 기본값은 no No
executable

fork를 yes로 하였을경우 javac가 실행될 경로를 나타내며 기본값으로는 ant에서 현재 사용중인 java version의 컴파일러를 나타낸다

No
memoryInitialSize

VM내에서 초기 메모리 사이즈를 설정하며 기본값은 VM에 설정된 기본값을 사용한다

No
memoryMaximumSize VM내에서 최대 메모리 사이즈를 설정 No
failonerror

컴파일시 에러가 발생하였을 경우 계속 진행할지 여부를 나타내며 기본값은 true

No
source -source 옵션을 이용하여 컴파일 한다 No
compiler

사용할 컴파일러 설정 list

No
listfiles

컴파일 되는 소스파일들을 list할지 여부를 나타내며 기본값은 no

No
tempdir

ant가 사용하는 임시디렉토리

기본값은 java.io.tmpdir

(ant 1.6이후)

No


2. <javadoc>

개요

-. 자바 소스 파일로 부터 javadoc API 문서를 생성한다


사용예제

간단한 사용예

<javadoc sourcepath="${src.home}"
               destdir="${dist.home}/docs/api"
               packagenames="*">
       <classpath refid="compile.classpath"/>
</javadoc>


아래 3가지 사용예는 동일한 의미이다

<javadoc

           packagenames="com.dummy.test.*"
           sourcepath="src"
           excludepackagenames="com.dummy.test.doc-files.*"
           defaultexcludes="yes"
           destdir="docs/api"
           author="true"
           version="true"
           use="true"
           windowtitle="Test API">
    <doctitle><![CDATA[<h1>Test</h1>]]></doctitle>
    <bottom><![CDATA[<i>Copyright &#169; 2000 Dummy Corp. All Rights Reserved.</i>]]></bottom>
    <tag name="todo" scope="all" description="To do:"/>
    <group title="Group 1 Packages" packages="com.dummy.test.a*"/>
    <group title="Group 2 Packages" packages="com.dummy.test.b*:com.dummy.test.c*"/>
    <link offline="true" href="http://java.sun.com/products/jdk/1.2/docs/api/" packagelistLoc="C:\tmp"/>
    <link href="http://developer.java.sun.com/developer/products/xml/docs/api/"/>
</javadoc>


<javadoc
           destdir="docs/api"
           author="true"
           version="true"
           use="true"
           windowtitle="Test API">

    <packageset dir="src" defaultexcludes="yes">
      <include name="com/dummy/test/**"/>
      <exclude name="com/dummy/test/doc-files/**"/>
    </packageset>

    <doctitle><![CDATA[<h1>Test</h1>]]></doctitle>
    <bottom><![CDATA[<i>Copyright &#169; 2000 Dummy Corp. All Rights Reserved.</i>]]></bottom>
    <tag name="todo" scope="all" description="To do:"/>
    <group title="Group 1 Packages" packages="com.dummy.test.a*"/>
    <group title="Group 2 Packages" packages="com.dummy.test.b*:com.dummy.test.c*"/>
    <link offline="true" href="http://java.sun.com/products/jdk/1.2/docs/api/" packagelistLoc="C:\tmp"/>
    <link href="http://developer.java.sun.com/developer/products/xml/docs/api/"/>
</javadoc>


<javadoc
           destdir="docs/api"
           author="true"
           version="true"
           use="true"
           windowtitle="Test API">

    <fileset dir="src" defaultexcludes="yes">
      <include name="com/dummy/test/**"/>
      <exclude name="com/dummy/test/doc-files/**"/>
    </fileset>

    <doctitle><![CDATA[<h1>Test</h1>]]></doctitle>
    <bottom><![CDATA[<i>Copyright &#169; 2000 Dummy Corp. All Rights Reserved.</i>]]></bottom>
    <tag name="todo" scope="all" description="To do:"/>
    <group title="Group 1 Packages" packages="com.dummy.test.a*"/>
    <group title="Group 2 Packages" packages="com.dummy.test.b*:com.dummy.test.c*"/>
    <link offline="true" href="http://java.sun.com/products/jdk/1.2/docs/api/" packagelistLoc="C:\tmp"/>
    <link href="http://developer.java.sun.com/developer/products/xml/docs/api/"/>
</javadoc>


속성

속성 설명 사용범위 필수여부
sourcepath 자바 소스 위치 all At least one of the three or nested <sourcepath>, <fileset> or <packageset>
sourcepathref 참조된 경로 아이디를 사용하여 소스위치를 지정 (path 의 refid를 사용) all
sourcefiles 컴마로 구분하여 소스 파일을 지정 all
destdir 결과물(java docs) 디렉토리를 지정 all Yes, unless a doclet has been specified.
maxmemory

javadoc VM에 할당할 최대 메모리 사이즈

all No
packagenames

컴마로 구분된 패키지 파일 목록

("*"로 끝나면 하위 패키지까지 모두 javadoc으로 처리한다)

all No
packageList 처리할 패키지를 포함하고있는 파일명 all No
classpath 사용할 클래스 패스 all No
Bootclasspath

사용할 bootstrap 클래스 패스

all No
classpathref 참조된 경로 아이디를 사용하여 클래스 패스를 지정 (path 의 refid) all No
bootclasspathref 참조된 경로 아이디를 사용하여  bootstrap classpath를 지정 all No
Extdirs 설치된 extension 위치 all No
Overview

HTML 문서로부터 overview 문서를 읽어온다

all No
access

Access mode를 지정한다

public, protected, package, private 중 하나의 값을 가지며 기본값은 protected이다

all No
Public 생성된 문서에 public 클래스와 멤버만 보여준다 all No
Protected 생성된 문서에 protected/public 클래스와 멤버만 보여준다 all No
Package 생성된 문서에 package/protected/public 클래스와 멤버만 보여준다 all No
Private 생성된 문서에 모든 클래스와 멤버를 보여준다 all No
Old jdk 1.1 버젼으로 문서를 생성한다 1.2 No
Verbose javadoc을 생성하는동안 메세지를 화면에 출력한다 all No
Locale

사용할 Locale 을 지정한다

(en_US, en_US_WIN, ko_KR)

all No
Encoding 소스파일 encoding 이름 all No
Version @version 단락을 포함한다 all No
Use 클래스와 패키지 사용법에 대한 페이지를 생성한다 all No
Author @author 단락을 포함한다 all No
Splitindex Split index into one file per letter all No
Windowtitle 생성된 문서의 브라우져 타이트을 지정한다 (텍스트) all No
Doctitle

패키지 인덱스 페이지에 대한 타이틀을 지정한다 (html 코드)

all No
Header

각페이지에대한 헤더부분을 지정한다 (html 코드)

all No
Footer 각페이지에대한 푸터부분을 지정한다 (html 코드) all No
bottom 각페이지에대한 버텀부분을 지정한다 (html 코드) all No
link

주어진 URL로 javadoc 출력물에 link를 생성한다

all No
linkoffline Link to docs at <url> using package list at <url2> - separate the URLs by using a space character -- see also the nested link element. all No
group 특정 패키지들에 대한 overview 페이지를 생성한다 all No
nodeprecated @deprecated 정보를 포함하지 않는다 all No
nodeprecatedlist deprecated 목록을 생성하지 않는다 all No
notree 클래스 하이라키를 생성하지 않는다 all No
noindex 인덱스를 생성하지 않는다 all No
nohelp 도움말 link를 생성하지 않는다 all No
nonavbar 네비게이션 바를 생성하지 않는다 all No
serialwarn @serial tag 대한 warning을 생성한다 all No
helpfile help 파일로 사용될 HTML 파일을 지정한다 all No
stylesheetfile CSS 로 사용할 파일을 지정한다 all No
charset 문서를 생성할때 사용될 charset을 지정한다 all No
docencoding 결과 파일(문서)에 사용될 encoding을 지정한다 all No
doclet

doclet을 시작하는 클래스 파일을 지정한다

all No
docletpath

doclet 클래스 패스를 지정한다

all No
docletpathref 참조 아이디값을 이용해 docletpath를 지정한다 all No
additionalparam

javadoc 명령에서 추가적인 파라미터를 추가하도록 한다

all No
failonerror

에러 발생시 build 중단할지 여부를 나타낸다

all No
excludepackagenames 문서를 생성하지 않을 패키지를 컴마로 구분하여 지정한다ment. all No
defaultexcludes

기본적으로 제외되는 목록을 사용할 것인지 여부를 나타낸다

all No
useexternalfile

명령줄을 간단히하기위해 임시파일에 기록된 소스파일명을 지정한다

기본값은 No

all No
source

j2se 1.4 소스코드에 나타난 주장을 처리하도록 javadoc에서 설정한다

1.4+ No
linksource

소스파일에 hyperlinks 생성한다

기본값은 No

(ant 1.6이후)

1.4+ No
breakiterator

새로운 breakiterator 알고리즘을 사용한다 기본값은 No

(ant 1.6이후)

1.4+ No
noqualifier

-noqualifier 옵션을 설정한다

(ant 1.6이후)

1.4+ No
includenosourcepackages

"true"이면 자바소스가 아닌 패키지된 html을 가지고 문서 생성이 한다

(ant 1.6.3이후)

all No (default is false)
executable

실행될 javadoc 위치를 지정한다

(ant 1.6이후)

all No


3. <war>

개요

-. 웹 어플리케이션 배포 파일인 war 파일을 생성해 준다


사용예제


thirdparty/libs/jdbc1.jar
thirdparty/libs/jdbc2.jar
build/main/com/myco/myapp/Servlet.class
src/metadata/myapp.xml
src/html/myapp/index.html
src/jsp/myapp/front.jsp
src/graphics/images/gifs/small/logo.gif
src/graphics/images/gifs/large/logo.gif


위의 파일을 WAR로 구성해 보자


<war destfile="myapp.war" webxml="src/metadata/myapp.xml">
  <fileset dir="src/html/myapp"/>
  <fileset dir="src/jsp/myapp"/>
  <lib dir="thirdparty/libs">
    <exclude name="jdbc1.jar"/>
  </lib>
  <classes dir="build/main"/>
  <zipfileset dir="src/graphics/images/gifs"
              prefix="images"/>
</war>


WAR task 후 다음과 같이 구성된다


WEB-INF/web.xml
WEB-INF/lib/jdbc2.jar
WEB-INF/classes/com/myco/myapp/Servlet.class
META-INF/MANIFEST.MF
index.html
front.jsp
images/small/logo.gif
images/large/logo.gif



속성

속성 설명 필수여부
destfile 생성될 WAR 파일 지정 둘중 하나는 필수
warfile Deprecated
webxml 사용할 deployment descriptor (WEB-INF/web.xml)를 지정 Yes
basedir jar파일로 압축할 디렉토리를 지정한다 No
compress

data를 압축할지 여부를 나타내며 기본값은  true이다

keepcompression 속성을 false로 했을지라도 이는 적용된다

No
keepcompression

이미 존재하는 파일 묶음에 대해 compress을 계속 유지할 것인지 여부를 나타내며 기본값은 fale 이다

(ant 1.6이후)

No
encoding

압축될 파일명에 사용할 encoding을 지정한다

기본값은 UTF8

No
filesonly

Store only file entries

기본값은 false

No
includes 지정된 컴마나 스페이스로 구분된 파일 패턴을 WAR에 포함하며 생략하면 모든 파일을 WAR한다 No
includesfile 지정된 개개의 파일을 WAR에 포함한다 No
excludes 지정된 컴마나 스페이스로 구분된 파일 패턴을 WAR에서 제외시키며 생략하면 어떠한 파일로 제외하지 않는다 No
excludesfile 지정된 개개의 파일을 WAR에서 제외시킨다 No
defaultexcludes 기본적으로 제외시키는 기능을 사용할것인지 여부를 나타낸다 No
manifest 사용할 manifest 파일을 지정한다 No
update 이미 존재하는 파일이라면 업데이트할지 덮어쓸지를 결정하며 기본값은 false이다 No
duplicate

중복된 파일이 나타났을 때 "add"할지 "preserve"할지 혹은 "fail"할지를 지정하며 기본값은 "add"이다

No
roundup

파일 시간 비교시 파일 변경 시간을 다음 짝수 횟수의 초(seconds)에 반올림 할지 혹은 내림할지 여부를 나타낸다

기본값은 true
(ant 1.6.2 이후)

No


from http://ant.apache.org/manual/index.html


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형



 

I. Welcom to Lucene !


  Lucene 은 자바로 구현된 고성능의 풀텍스트 검색엔진입니다

  Lucene 이 제공하는 API를 사용하여 강력하고 유연한 검색기능을 어플리케이션에 손쉽게 추가할 수 있습니다

  Lucene은 2005년 2월에 Apache top-level 로 등급이 상승하였고

  서브 프로젝트인 Nutch 또한 2005년 6월에 Apache Incubator를 졸업하였습니다


  Lucene는 다음 3가지 software를 포함하고 있습니다

  Lucene java : Lucene의 핵심 부분으로 indexing 과 search 구현 영역입니다

  Nutch : web search application을 지원합니다 (이슈!)

                 mp3, pdf, ms 등 다양한 문서를 검색할수 있도록 제공해 주지만

                 아직까지는 0.6 버젼이네요

  Lucene4c :  Lucene의 C 기반의 검색엔진입니다 아직까진 Incubator에 있군요


  이번 강좌에서는 Lucene java 함 뒤벼보고 웹에다 Lucene을 달아봅시다 ^^


  강좌 진행 순서는 "데모I 프로그램(어플리 케이션) -> 데모II 프로그램(웹 어플리케이션) -> 데모II 프로그램 수정" 순으로 갑니다


  ps. 한글검색이 가능하지만 한글 형태소 분석을 아직까지는 지원하지 않기 때문에

       한글은 단순검색으로 만족해야 합니다.

       하지만 영문은 기똥차게 잘됩니다



II. Download


  lucene 다운로드

  http://www.apache.org/dyn/closer.cgi/jakarta/lucene/binaries/


  참고 사이트

  http://lucene.apache.org/

  http://lucene.apache.org/java/docs/api/index.html

  http://lucene.apache.org/java/docs/index.html

  http://today.java.net/pub/a/today/2003/11/07/QueryParserRules.html

  http://www-128.ibm.com/developerworks/library/j-lucene/

  http://www.onjava.com/pub/a/onjava/2003/03/05/lucene.html




III. 설치


  그럼 이제 Lucene에 한번 빠져 봅시다~!

  다운받은 lucene-1.4.3.zip 파일을 C:\에 압축을 풉니다




  압축을 풀면 lucene-1.4.3.jarlucene-demos-1.4.3.jar 파일을 클래스 패스에 겁니다



C:\Documents and Settings\Administrator>cd c:\

C:\>cd lu*

C:\lucene-1.4.3>set classpath=C:\lucene-1.4.3\lucene-1.4.3.jar;C:\lucene-1.4.3\lucene-demos-1.4.3.jar


C:\lucene-1.4.3>set
ALLUSERSPROFILE=C:\Documents and Settings\All Users
ANT_HOME=C:\java\Jeus42\lib\etc\ant
APPDATA=C:\Documents and Settings\Administrator\Application Data
CLASSPATH=C:\lucene-1.4.3\lucene-1.4.3.jar;C:\lucene-1.4.3\lucene-demos-1.4.3.jar

...



  "set" 명령으로 확인합니다

  설치 끝 ~



IV. 데모I 실행하기


  Lucene 데모에는 두가지 데모가 있습니다

  하나는 일반적인 인덱싱, 및 검색이고 다른 하나는 웹에서 사용하기 위한 인덱싱 및 웹검색입니다


1) 데모I 실행


먼저 네이버나 야후같은 웹 검색엔진을 생각해 봅시다
수도없이 많은 문서들이 어떻게 해서 그렇게 빠릴 검색될까요?
바로 검색 전처리 작업을 하기 때문입니다
예를들어 간단하게 보자면 "love"라는 단어는 A문서, B문서, C문서에 포함되어 있다라는 정보를 미리 만들어 두는 것입니다
즉 인덱스를 만들어 두는 것이지요
그리고 웹로봇들이 문서를 수집해오면 추가된 문서들에 대해 하루에 몇번씩 배치작업으로 인덱스를 추가해 주겠지요
결과적으로 "love" 검색시 인덱스 정보를 뒤져서 A문서, B문서, C문서의 결과를 보여주는 겁니다


lucene도 마찬가지 입니다 인덱스를 먼저 만들어 주어야 합니다
먼저 일단 데모프로그램을 이용하여 인덱스를 만들어 보고 이를 이용하여 검색해 봅시다
내부 코딩은 일단 실행 이후에 살펴봅시다 ^_^ (눈에 먼저 보여야 멀 해도 잘되죵)


C:\lucene-1.4.3\src\ 는 데모을 위한 자바소스 파일입니다


 

인덱스 생성하기
다음 명령으로 인덱스를 생성해 봅시다
인덱스 생성 어플리케이션은 IndexFiles.java 이며 파라미터는 인덱스를 만들 소스파일들(검색대상파일들) 입니다
여기서는 lucene의 document를 index 처리해 보겠습니다


C:\lucene-1.4.3>
C:\lucene-1.4.3>java org.apache.lucene.demo.IndexFiles C:\lucene-1.4.3\docs
adding C:\lucene-1.4.3\docs\api\allclasses-frame.html
adding C:\lucene-1.4.3\docs\api\allclasses-noframe.html
adding C:\lucene-1.4.3\docs\api\constant-values.html
adding C:\lucene-1.4.3\docs\api\deprecated-list.html
adding C:\lucene-1.4.3\docs\api\help-doc.html
adding C:\lucene-1.4.3\docs\api\index-all.html
adding C:\lucene-1.4.3\docs\api\index.html
adding C:\lucene-1.4.3\docs\api\org\apache\lucene\analysis\Analyzer.html
...
18500 total milliseconds
C:\lucene-1.4.3>


야호~ 인덱스가 생성되었습니다



C:\lucene-1.4.3\index 풀더를보면 다음 파일들이 생성된 것을 알수있습니다


검색하기
검색 어플리케이션은 SearchFiles.java 이며 실행 후 Query: 에 검색할 단어를 입력해 봅시다

C:\lucene-1.4.3>
C:\lucene-1.4.3>java org.apache.lucene.demo.SearchFiles
Query: lucene
Searching for: lucene
324 total matching documents
0. C:\lucene-1.4.3\docs\api\allclasses-frame.html
1. C:\lucene-1.4.3\docs\api\allclasses-noframe.html
2. C:\lucene-1.4.3\docs\api\index-all.html
3. C:\lucene-1.4.3\docs\api\overview-frame.html
4. C:\lucene-1.4.3\docs\api\overview-tree.html
5. C:\lucene-1.4.3\docs\api\org\apache\lucene\queryParser\MultiFieldQueryParser.html
6. C:\lucene-1.4.3\docs\lucene-sandbox\index.html
7. C:\lucene-1.4.3\docs\resources.html
8. C:\lucene-1.4.3\docs\api\org\apache\lucene\search\class-use\Query.html
9. C:\lucene-1.4.3\docs\api\org\apache\lucene\index\class-use\Term.html
more (y/n) ?


와~ 성공! 엄청 빠릅니다!!

검색결과가 10개씩 리스팅되며 다음 리스트는 y버튼을 클릭하여 조회 할수 있습니다



2) 데모I 인덱싱 코드 분석

이 데모 프로그램은 가장 기본이 되는 프로그램으로 핵심 코딩만 되어 있으니 lucene을 사용하기 위해서는 꼭 알아 두어야 합니다


Analyzer 선택

Analyer 는 문서를 인덱싱 하거나 검색할때 핵심이 되는 요소로서, 텍스트를 파싱할 때 사용합니다 Analyzer의 종류에는 다음 몇가지 들이 있습니다


SimpleAnalyzer

  non-letters 를 기준으로 문자를 파싱합니다
  non-letters는 java.lang.Character.isLetter()에 의해 정의된 것을 사용합니다
  이 Analyzer는 대부분의 유럽권 언어에 적당하며 아시아권 언어에는 terrible 이라고 되어 있군요 -_-;
  대소문자 구분 안합니다


StopAnalyzer

  기본적으로 SimpleAnalyzer와 같으나 StopFilter를 두어 StopWord를 제거한 후 분석합니다
  StopWord란 일반적으로 검색시 유용하지 않는 단어들을 말합니다
  (a,an,and,are,as,at,be,but,by,for,if,in,into,is,it,no,not,of,on,or,s,such,t,that,the,their,then,there,these,they,this,to,was,will,with) 역시나 대소문자 구분 안합니다


StandardAnalyzer 

  대부분 유렵권 언어들에 최적화 되어있는 Analyzer 입니다
  StopFilter를 사용하며 대소문자 구분 안합니다


WhitespaceAnalyzer 

  whitespace, 즉 공백문자를 가지고 text를 나누는 방식입니다
  가장 기본적이면서도 무식한 방식입니다
  불행히도 한글검색은 이 WhitespaceAnalyzer 만 가능하며 대소문자 구분 합니다

  (왜 이것만 대소문자 구분 하냥 -_-)


이밖에도 GermanAnalyzer, PerFieldAnalyzerWrapper, RussianAnalyzer등이 있으며
이름에서도 알수있듯이 그다지 한글과 친하지 않은듯 합니다 -.-;;


IndexWriter 생성
자 이제 Analyer를 선택했으면 IndexWriter를 생성하여 Index를 만들어봅시다


IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true);


첫번째 파라미터는 index가 생성될 위치를 말하며
두번째 파라미터는 선택한 Analyzer를,
세번째 파라미터는 index를 초기화 하여 다시 생성할것인지 말것인지를 말합니다
즉 추가/삭제만 할 것인지(false) 새로 만들것인지(true)를 나타냅니다
문서가 많을경우 매번 새로 만들수 없으며,
또한 만약 몇개의 문서만 변경되었는데 모두 다시 인덱스를 만들수는 없기 때문입니다 (시간 상당히 걸림 --)


Index에 document 추가
다음으로 소스 디렉토리(검색대상 문서들)의 파일들을 읽어가며 Analyzer에 의해 파싱된 문서 정보를 인덱스에 추가합니다


writer.addDocument(FileDocument.Document(file));


index optimize
마지막으로 인덱싱한 정보를 하나의 파일로 merge 합니다
즉 검색에 적합하도록 파일을 하나로 합치는 겁니다


writer.optimize();


index close

사용후 받드시 close 합시다!


writer.close();


끝~ 간단하죠?

이제 전체 소스를 살펴봅시다

소스는 C:\lucene-1.4.3\src\demo\org\apache\lucene\demo 에 있습니다


org.apache.lucene.demo.IndexFiles.java


class IndexFiles {


  public static void main(String[] args) throws IOException {

    String usage = "java " + IndexFiles.class + " <root_directory>";
   
    // 파라미터(소스디렉토리)를 입력하지 않았다면 사용법을 출력해 줍니다
    if (args.length == 0) {
      System.err.println("Usage: " + usage);
      System.exit(1);
    }

    Date start = new Date();
    try {
   
      // 선택한 Analyzer를 이용하여 IndexWriter를 생성합니다
      IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true);
     
      // 소스디렉토리를 디비가며 문서를 파싱하여 인덱스에 추가하는 재귀함수
      indexDocs(writer, new File(args[0]));

      // 인덱싱한 여러 segment들을 검색에 알맞도록 하나로 합치는 작업
      writer.optimize();
     
      // 반드시 close()
      writer.close();

      Date end = new Date();

      // 인덱싱 처리시간 출력
      System.out.print(end.getTime() - start.getTime());
      System.out.println(" total milliseconds");

    } catch (IOException e) {
      System.out.println(" caught a " + e.getClass() +
       "\n with message: " + e.getMessage());
    }
  }


  // 소스 파일들을 읽어가며 파싱하여 인덱스에 추가하는 재귀함수
  public static void indexDocs(IndexWriter writer, File file)
    throws IOException {


    if (file.canRead()) {
      if (file.isDirectory()) {
        String[] files = file.list();


        if (files != null) {
          for (int i = 0; i < files.length; i++) {
            // 자기자신을 호출
            indexDocs(writer, new File(file, files[i]));
          }
        }
      } else {
        System.out.println("adding " + file);
        try {
          // 문서를 인덱스에 추가

          // FileDocument는 해당 file의 정보를 파싱해줌
          writer.addDocument(FileDocument.Document(file));
        }
       
        // 윈도에서 temporary filese등이 access denied로  exception이 발생할 수 있음
        catch (FileNotFoundException fnfe) {
          ;
        }
      }
    }
  }
}



3) 데모I 검색 코드분석
검색은 인덱스를 만드는 코드보다 훨씬 쉽습니다


IndexSearcher 생성
실질적으로 검색을 담당할 IndexSearcher를 생성해 봅시다
파라미터로는 index가 생성되어있는 위치를 입력합니다


Searcher searcher = new IndexSearcher("index");


Query 생성
Query는 질의 문자열을 파싱해 줍니다 (즉 AND,OR,NOT,!,-등의 논리연산이나 와일드카드 *,?등을 파싱합니다)


Query query = QueryParser.parse(line, "contents", analyzer);


첫번째 파라미터는 질의를,
두번째 파라미터는 검색 필드를,
세번째 파라미터는 인덱스를 만든 Analyzer와 동일한 Analyzer를 입력해 줘야 합니다


검색 및 검색한 결과 저장

파싱된 쿼리를 가지고 검색하여 그 결과값을 반환 받습니다


Hits hits = searcher.search(query);


반환된 Hits 클래스는 순위가 매겨진 문서들로 검색결과를 저장하고 유지합니다


IndexSearcher close
검색이 끝났으면 닫아줍시다


searcher.close();


전체 소스를 살펴봅시다


org.apache.lucene.demo.SearchFiles.java

class SearchFiles {


  public static void main(String[] args) {
    try {


      // index 폴더를 파라미터로 넘겨 IndexSearcher를 생성합니다
      Searcher searcher = new IndexSearcher("index");
     
      // 생성된 index와 동일한 Analyzer를 생성하여 넘겨줍니다
      Analyzer analyzer = new StandardAnalyzer();


      // 검색어를 입력받기 위한 reader 입니다
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));


      while (true) {
        System.out.print("Query: ");
        String line = in.readLine();
       
        if (line.length() == -1)
          break;


        // 입력받은 질의를 파싱합니다
        Query query = QueryParser.parse(line, "contents", analyzer);
        System.out.println("Searching for: " + query.toString("contents"));


        // 팡싱된 질의로 검색을 합니다       
        Hits hits = searcher.search(query);
        System.out.println(hits.length() + " total matching documents");
       
        // 검색 결과 목록을 10개씩 보여줍니다
        final int HITS_PER_PAGE = 10;
        for (int start = 0; start < hits.length(); start += HITS_PER_PAGE) {
          int end = Math.min(hits.length(), start + HITS_PER_PAGE);
          for (int i = start; i < end; i++) {


            // 검색 결과로 부터 문서를 가져옵니다
            Document doc = hits.doc(i);

            // 문서정보를 출력합니다

            String path = doc.get("path");
            if (path != null) {
                  System.out.println(i + ". " + path);
            } else {
                  String url = doc.get("url");
              if (url != null) {
                System.out.println(i + ". " + url);
                System.out.println("   - " + doc.get("title"));
              } else {
                System.out.println(i + ". " + "No path nor URL for this document");
              }
            }
          }


          if (hits.length() > end) {
            System.out.print("more (y/n) ? ");
            line = in.readLine();
            if (line.length() == 0 || line.charAt(0) == 'n')
              break;
          }
        }
      }
     
      searcher.close();
       
    } catch (Exception e) {
      System.out.println(" caught a " + e.getClass() +
         "\n with message: " + e.getMessage());
    }
  }
}



V. 질의 문법

질의 문법에 대해 알아봅시다


A AND B

    A와 B가 모두 포함된 문서를 검색한다

A OR B

    A혹은 B가 포함된 문서를 검색한다

A NOT B

    A는 포함되고 B는 포함되지 않는 문서를 검색한다

    A ! B, A - B와 동일하다

+A OR B

    A OR B에서 A는 받드시 포함된 문서를 검색한다

A*

    A로 시작하는 단어가 있는 문서를 검색한다

A?

    A로 시작하는 두글자의 단어가 있는 문서를 검색한다

A~

    A와 스펠링이 비슷한 글자를 지닌 단어가 있는 문서를 검색한다

(A OR B) AND C

    논리연산의 그루핑 또한 지원한다


AND, OR, NOT등은 반드시 대문자로 입력해야 인식됩니다


다음 시간에는 두번째 데모 프로그램을 실행시켜 보고 lucene을 웹 어플리케이션에 달아 봅시다~


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Jakarta Ant


I. Ant 야 놀자~

Ant 란 "Java 기반의 빌드 도구입니다" 라고들 많이 나와있습니다
근데 이게 대체 무순 뜻이란 말인가 ㅡ.ㅡ;


쉽게 예기해서 환경에 구애받지 않고 간단히 java 소스를 컴파일 하고 필요한 파일이 있다면
그 파일들을 복사나 삭제하며 또한 javadoc 이나 war 같은 배포용 파일까지 생성해 주며
필요하다면 서버에 까지 바로 적용할 수 있도록 도와주는 도구입니다.


또한 여러팀원들이 프로젝트를 진행할 때 새로운 팀원이 참가하더라도
Ant 설정파일을 공유함으로써 손쉽게 참여가 가능하다는 것입니다


아직 사용해 보지 않았다면 얼렁 익혀서 사용해봅시다 무~지 편하답니다



II. 설치해보자
다운로드
Ant

http://ant.apache.org/bindownload.cgi

Ant manual

http://ant.apache.org/manual/index.html

http://ant.apache-korea.org/manual/index.html


다운받은 파일의 압축을 풀고 환경변수 및 패스를 잡아줍니다
set ANT_HOME=c:\ant
set JAVA_HOME=c:\jdk1.4.2
set PATH=%PATH%;%ANT_HOME%\bin



III. 간단한 Ant 예제

Ant를 이용하여 web application을 구성할 때 다음의 구조를 유지하기를 권장합니다


build : src, web, docs에서 결과적으로 만들어진 산출물 디렉토리
dist : build를 배포하기 위한 배포 디렉토리
docs : 배포판에 배포할 정적인 문서를 관리할 디렉토리
src : /WEB-INF/classes 에 위치할 java 소스 디렉토리
web : HTML, JSP, 이미지등의 컨텐트 디렉토리 (WEB-INF의 서브디렉토리 포함)
build.properties : build.xml에서 사용할 properties
build.xml : ant 명령으로 실행될 설정파일


src에 하나이상의 java 소스를 테스트로 넣어 놓으세요

자 이렇게 디렉토리를 설정하고 build.xml 을 다음 step에 따라 따라 해 BOA요 ^^&


STEP 1. build.xml 의 기본구조

xml을 기본적인 내용을 안다면 이해하기 쉽습니다

<project name="My Project" default="compile" basedir=".">

 <target name="clean">

   여러 작업

  </target>


  <target name="prepare" depends="clean">

   여러 작업

   </target>


   <target name="compile" depends="prepare">

    여러 작업

    </target>

</project>

하나의 build 파일은 하나의 project로 구성되며 이는 다시 여러 target으로 구성됩니다

target 이란 빌드 과정중 수행해야 할 task들을 모아놓은 job 단위 라고 보면 됩니다

compile target이라 한다면 compile에 관련된 작업들을 모아놓은 그룹이라 생각하면 쉽게 이해 될겁니다


STEP 2. 시~작 Ant 맛보기~
① build.xml에 다음을 입력한 후 저장 합니다

<project name="Unicorn Project" default="clear" basedir=".">
  <description>Simple Test</description>
 
  <property file="${basedir}/build.properties"/>
 
  <echo message="${catalina.home}"/>


   <target name="clear"/>
   
</project>

-. project

    project는 하나 이상의 target을 정의 합니다 또한 하나의 target은 task의 집합입니다

    ant를 실행할 시에 어느 타겟을 실행할 것인지 지정할 수가 있으며 (예: \ant clear)

    지정하지 않았을 경우 디폴트로 설정된 값이 사용됩니다 이부분이 default="clear"입니다

-. property

    전역변수 설정 혹은 그렇게 사용할 build.properties를 정의 합니다

    build.properties에 catalina.home을 정의하였으며 여러 환경이 변하더라도 이 값만

    변경해주면 build.xml을 수정없이 바로 실행 가능합니다

-. echo

    message 내용을 출력 합니다

-. target

    target 이란 task의 집합으로 실질적으로 실행될 코드들의 묶음입니다

    여기서는 아무 task도 없습니다


② build.properties에 다음을 입력 후 저장합니다

catalina.home=C:\Tomcat 5.0

catalina.home 은 변수로 사용할 것이며 그 값은 C:\Tomcat 5.0입니다


③ 실행

해당 디렉토리로 이동하여 도스창에서 ant 라고 칩니다

c:\예제\ant

Buildfile : build.xml

      [echo] c:\Tomcat 5.0


clear :


BUILD SUCCESSFUL

Total time : 0 seconds


STEP 3. 사전작업 하기~

이번 단계에서는 컴파일 하기전 전역변수 선언이나 컴파일 시 클래스 패스 설정을 해봅시다


① build.xml

<project name="Unicorn Project" default="prepare" basedir=".">
  <description>Simple Test</description>

  <property file="${basedir}/build.properties"/>


  <property name="app.name"      value="unicorn"/>
  <property name="app.path"       value="/${app.name}"/>
  <property name="app.version"   value="0.1-dev"/>

  <property name="build.home"    value="${basedir}/build"/>
  <property name="dist.home"     value="${basedir}/dist"/>
  <property name="docs.home"   value="${basedir}/docs"/>
  <property name="src.home"      value="${basedir}/src"/>
  <property name="web.home"     value="${basedir}/web"/>  


  <path id="compile.classpath">

    <pathelement location="${catalina.home}/common/classes"/>
    <fileset dir="${catalina.home}/common/endorsed">
      <include name="*.jar"/>
    </fileset>
    <fileset dir="${catalina.home}/common/lib">
      <include name="*.jar"/>
    </fileset>
    <pathelement location="${catalina.home}/shared/classes"/>
    <fileset dir="${catalina.home}/shared/lib">
      <include name="*.jar"/>
    </fileset>
    <fileset dir="${web.home}/WEB-INF/lib">
      <include name="*.jar"/>
    </fileset>
  </path>


  <target name="clean"
   description="Delete old build and dist directories">
    <delete dir="${build.home}"/>
    <delete dir="${dist.home}"/>
  </target>
 
  <target name="prepare" depends="clean">

    <mkdir dir="${build.home}"/>
    <mkdir  dir="${build.home}/WEB-INF"/>
    <mkdir  dir="${build.home}/WEB-INF/classes"/>
    <mkdir  dir="${build.home}/WEB-INF/lib"/>
   
    <copy todir="${build.home}">
       <fileset dir="${web.home}">
         <include name="**/*.jsp"/>
         <include name="**/*.css"/>
         <include name="**/*.html"/>
         <include name="**/*.js"/>
         <include name="**/*.jpg"/>
         <include name="**/*.gif"/>
         <include name="**/*.swf"/>
         <include name="**/*.xml"/>
         <include name="**/*.tld"/>
         <include name="**/*.jar"/>

       </fileset>
     </copy>

   </target>  

   
</project>

-. project

    이번에는 default 값을 prepare로 하였습니다 고로 target은 prepare가 실행될 것입니다

-. property

    역시나 build.properties를 정의하였고 여러 전역변수를 설정하였습니다

    build.home 이란 변수에는 ${basedir}/build 값이 정의되었으며

    build.home은 ${build.home}으로 사용할수 있습니다

    궁금하면 <echo message="${build.home}"/> 등으로 출력해 봅시다~

-. path

    ${catalina.home} 은 build.properties에서 정의하였다는것을 기역하실겁니다

    fileset은 파일들의 집합을 나타내는데 어떤 특정파일만 포함 할수 있거나 혹은 어느 특정파일만 제외할 수 있습니다

    특정파일만 포함하는것은 include 태그이며 그 반대는 exclude 입니다

    <fileset dir="${web.home}/WEB-INF/lib">
      <include name="*.jar"/>
    </fileset>

    이부분은 ${web.home}/WEB-INF/lib 에서 *.jar 파일만 클래스패스로 잡겠다는 의미입니다

-. target

   드디어 실질적으로 실행될 target 입니다.

   project 속성의 default가 prepare이므로 prepare target으로 실행하기 위해 가보니  depend 속성으로 "clean"이 설정되어 있습니다

  이것은 "clean" target을 먼저 실행 후 prepare를 실행하라는 말입니다

   즉 prepare는 clean이 실행조건 입니다

   clean target은 delete 태그를 이용하여 배포할 dist 디렉토리와 산출물 디렉토리인 build를 삭제하는 작업입니다
   prepare target으로 다시 와보면 mkdir 는 당연히 디렉토리를 만들라는 뜻입니다

   copy는 todir에 다음 web.home의 모든 fileset을 copy 하라는 의미입니다 여기서도 역시나 include, exclude가 사용될 수 있겠지요

   <include name="**/*.jsp"/> 는 모든디렉토리의 *.jsp 를 카피하라는 의미입니다

    즉 web 디렉토리의 모든 실행 가능할 파일을 build로 옮기는 작업입니다


② 실행

c:\예제\ant

Buildfile : build.xml


clean :

     [delete] : Deleting directory c:\예제\build

     [delete] : Deleting directory c:\예제\dist


prepare :

     [mkdir] Created dir : c:\예제\build\WEB-INF

     [mkdir] Created dir : c:\예제\build\WEB-INF\classes

     [mkdir] Created dir : c:\예제\build\WEB-INF\lib

     [copy] Copying 2 files to Deleting directory c:\예제\build


BUILD SUCCESSFUL

Total time : 1 seconds


STEP 4. 컴파일 하기

이제 컴파일할 모든 준비가 되었습니다

STEP2의 소스에 다음 target을 추가하고 project의 default 값을 compile로 수정한 후 실행해 봅시다


 <target name="compile" depends="prepare"
   description="Compile Java sources">


    <!-- Compile Java classes as necessary -->
    <javac srcdir="${src.home}"
          destdir="${build.home}/WEB-INF/classes">
        <classpath refid="compile.classpath"/>
    </javac>


    <!-- Copy application resources -->
    <copy  todir="${build.home}/WEB-INF/classes">
      <fileset dir="${src.home}" excludes="**/*.java"/>
    </copy>
   
 </target>

-. target

   depends가 prepare이므로 실행 순서는 clean, prepare, compile 입니다

   javac 태스크는 srcdir 인 소스디렉토리에 있는 자바소스를 desdir 쪽으로 컴파일 합니다

   classpath는 위에서 이미 설정한 compile.classpath를 사용합니다

   그 후 properties등 같은 기타 resource 들을 /WEB-INF/classes에 복사합니다

② 실행

c:\예제\ant

Buildfile : build.xml


clean :

     [delete] : Deleting directory c:\예제\build

     [delete] : Deleting directory c:\예제\dist


prepare :

     [mkdir] Created dir : c:\예제\build\WEB-INF

     [mkdir] Created dir : c:\예제\build\WEB-INF\classes

     [mkdir] Created dir : c:\예제\build\WEB-INF\lib

     [copy] Copying 2 files to Deleting directory c:\예제\build


compile :

     [javac] Compileing 1 source file to c:\예제\build\WEB-INF\classes

     [copy] Copying 1 file to c:\예제\build\WEB-INF\classes


BUILD SUCCESSFUL

Total time : 1 seconds


STEP 5. Javadoc 을 이용해 API를 만들어 보고 배포파일도 생성해 보자

이제 컴파일도 했으니 javadoc도 만들어 보고 war 같은 배포파일도 생성해 봅시다

STEP 3.까지의 build.xml에 아래 target을 더 추가하고 project의 default 값에 dist로 설정합니다

① build.xml

 <target name="javadoc" depends="compile"
    description="Create Javadoc API documentation">

     <mkdir dir="${dist.home}/docs/api"/>
     <javadoc sourcepath="${src.home}"
                  destdir="${dist.home}/docs/api"
                   packagenames="*">
       <classpath refid="compile.classpath"/>
     </javadoc>

   </target>


 <target name="dist" depends="compile,javadoc"
   description="Create binary distribution">

    <mkdir dir="${dist.home}/docs"/>

    <copy  todir="${dist.home}/docs">
      <fileset dir="${docs.home}"/>
    </copy>


    <jar jarfile="${dist.home}/${app.name}-${app.version}.war"
         basedir="${build.home}"/>

  </target>

-. target

   target의 실행순서는 clean, prpare, compile, javadoc, dist가 될겁니다

   javadoc target의 javadoc 태스트를 보면 java 소스가 있는 소스디렉토리와

   API를 생성할 타겟 디렉토리를 정해주면 알아서 API를 생성해 줍니다

   만들어진 API는 배포버젼의 dist디렉토리로 해주면 더 좋겠지요


   dist target은 배포파일인 war를 만듭니다

   필요한 문서가 있으면 docs 디렉토리를 만들어 로 복사도 하도록 합시다

   jar 태스크는 위의 방식과 같이 사용합니다


② 실행

c:\예제\ant

Buildfile : build.xml


clean :

     [delete] : Deleting directory c:\예제\build

     [delete] : Deleting directory c:\예제\dist


prepare :

     [mkdir] Created dir : c:\예제\build\WEB-INF

     [mkdir] Created dir : c:\예제\build\WEB-INF\classes

     [mkdir] Created dir : c:\예제\build\WEB-INF\lib

     [copy] Copying 2 files to Deleting directory c:\예제\build


compile :

     [javac] Compileing 1 source file to c:\예제\build\WEB-INF\classes

     [copy] Copying 1 file to c:\예제\build\WEB-INF\classes


javadoc :

     [mkdir] Created dir : c:\예제\dist\docs\api

     [javadoc] Generating Javadoc

     [javadoc] Javadoc execution

     [javadoc] Loading source files for package com.jakartaproject.db...

     [javadoc] Constructing Javadoc information...

     [javadoc] Standard Doclet version 1.4.1

     [javadoc] Building tree for all the packages and classes...

     [javadoc] Building index for all the packaes and classes...

     [javadoc] Building index for all classes...


dist :

     [jar] Building jar :Created dir : c:\예제\dist\unicorn-0.1-dev.war


BUILD SUCCESSFUL

Total time : 3 seconds



VI. Ant 실행

C:\예제\ant -help

ant [options] [target [target2 [target3] ...]]


Options :
  -help                    이 메세지의 표시
  -projecthelp           프로젝트 도움 정보의 출력
  -version                버전 정보의 출력과 종료
  -diagnostics           diagnose 나 report 문제에  도움이 되는 정보의 출력.
  -quiet, -q              한층 더 메세지를 적게
  -verbose, -v          한층 더 메세지를 많게
  -debug                 디버그 정보의 출력
  -emacs                 adornments 없이 로그 정보의 생성(produce)
  -logfile <file>         로그를 지정 파일에 출력
    -l     <file>                ''
  -logger <classname>    로그 생성을 실행하기 위한 클래스
  -listener <classname>  프로젝트 청취자(listener) 역할의 class의 인스턴스를 추가
  -buildfile <file>            지정된 빌드 파일의 사용
    -file    <file>              ''
    -f       <file>              ''
  -D<property>=<value> 지정된 프로퍼티의 값의 사용
  -propertyfile <name>    모든 프로퍼티를 파일로부터 로드  (-D프로퍼티보다 전에)
  -inputhandler <class>  입력 요청(requests)를 취급하는 클래스
  -find <file>                 파일시스템의 루트로 향해 빌드파일을  검색하고 그것을 사용


C:\예제\ant

현재 디렉토리에 있는 build.xml 파일을 이용해, 디폴트 타겟으로 Ant 를 실행합니다.


C:\예제\ant compile

현재 디렉토리에 있는 build.xml이 실행되며 파라미터로 compile을 지정하면 project의 default 값을 무시하고 compile target을 실행합니다 물론 depends 가 있다면 먼저 실행합니다

 

C:\예제\ant -buildfile test.xml

현재 디렉토리에 있는 test.xml 파일을 이용해, 디폴트 타겟으로 Ant 를 실행합니다.


C:\예제\ant -buildfile test.xml dist

현재 디렉토리에 있는 test.xml 파일을 이용해, dist 라는 이름의 타겟으로 Ant 를 실행합니다.


C:\예제\ant -buildfile test.xml -Dbuild=build/classes dist

현재 디렉토리에 있는 test.xml 파일을 이용해, dist 라는 이름의 타겟으로, build 프로퍼티에 build/classes 값을 설정해 Ant 를 실행합니다.


VII. 기본 프로퍼티

Ant는 다음과 같은 프로퍼티를 기본으로 제공합니다

basedir : 기본 디렉토리 경로를 나타내며 project 태그의 basedir에 명시되어 있습니다

ant.file : 빌드 파일의 절대 경로입니다

ant.version : Ant 버젼을 나타냅니다

ant.project.name : project 태그의 name에 명시되어있습니다

ant.java.version : 자바 버젼을 나타냅니다

자바 시스템 프로퍼티를 사용 가능 합니다

   예) ${os.name} 참고 http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#getProperties()


<echo message="${ant.file}"/> 으로 한번 출력해 보면 이해가 쉽습니다


다음 시간에는 각 타스크의 종류와 상세 설정에 관해 알아봅시다


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Commons net


I. Commons net

Jakarta Commons의 net은 network utility collection입니다.

인터넷 프로토콜을 대부분지원하는 강력한 유틸리티로 사용이 쉽고 간결합니다.


클라이언트측의 기본적인 Internet protocol을 구현함으로서 고차원적인 abstract가 아닌 기본적인 프로토콜 access가 목적이기 때문에 부분적으로 object-orient 규칙에 위배되는 사항이 있다는것을 참고적으로 알자


Commons net이 지원하는 프로토콜은 다음과 같다

  • FTP
  • NNTP
  • SMTP
  • POP3
  • Telnet
  • TFTP
  • Finger
  • Whois
  • rexec/rcmd/rlogin
  • Time (rdate) and Daytime
  • Echo
  • Discard
  • NTP/SNTP

FTP의 경우 이전에는 sun 패키지의 FtpClient를 사용했지만 이제는 Commons net의 FTP를 사용해 봅시다


II. 준비물~

Commons net은 자체적으로 ORO를 사용합니다

Jakarta ORO란 파일이름의 치환, 분할, 필터링 등을 수행하기 위한 Perl5 호환 정규식, AWK와 같은 정규식, glob 표현, 그리고 유틸리티 클래스들을 제공하는 텍스트 처리 자바 클래스들입니다

 

Commons net http://jakarta.apache.org/site/downloads/downloads_commons-net.cgi

Jakarta ORO http://jakarta.apache.org/site/downloads/downloads_oro.cgi


참고자료

Commons net API http://jakarta.apache.org/commons/net/apidocs/index.html

Commons net http://jakarta.apache.org/commons/net/


III. org.apache.commons.net.ftp 의 흐름

sun 패키지의 FTPClient처럼 FTP여러 동작을 Commons net의 FTPClient로 구현해 보자


다음 ① ~ ⑦까지 목록은 필수항목으로 구현되어야 합니다.

논리적으로 보면 간단합니다

FTPClient 생성

먼저 가장 중심적인 역할을 하는 FTPClient를 생성합니다

FTPClient ftpClient = new FTPClient();


FTPServer에 Connect

서버에 연결을 합니다

ftpClient.connect(server);


응답이 정상적인지 확인 합니다

응답을 받아와서

int reply = ftpClient.getReplyCode();


정상적인지 확인해 봅니다
if (!FTPReply.isPositiveCompletion(reply)) {

    정상적이지 않으면 연결을 끊고 종료 합니다

    ftpClient.disconnect();


    System.out.println("FTP server refused connection.");

}  else {

    정상적이면 계속 진행 합니다
    System.out.println("Connect successful");

    ..

}


FTP Server 로그인

ftpClient.login(username, password);


여러가지 작업을 합니다

list, get, put...


FTP Server 로그아웃

ftpClient.logout();


FTP Server disconnect

ftpClient.disconnect();


IV. 사용예제 - 목록보기

본격적으로 FTP에 접속하여 목록을 가져오는 소스를 봅시다

FTPClient ftpClient = null;

try {

    ftpClient = new FTPClient();

    ftpClient.setControlEncoding("euc-kr");  // 한글파일명 때문에 디폴트 인코딩을 euc-kr로 합니다

    ftpClient.connect("user.chollian.net");  // 천리안 FTP에 접속합니다


   int reply = ftpClient.getReplyCode(); // 응답코드가 비정상이면 종료합니다
   if (!FTPReply.isPositiveCompletion(reply)) {
       ftpClient.disconnect();
       System.out.println("FTP server refused connection.");    

   } else {


       System.out.print(ftpClient.getReplyString());  // 응답 메세지를 찍어봅시다


       ftpClient.setSoTimeout(10000);  // 현재 커넥션 timeout을 millisecond 값으로 입력합니다
       ftpClient.login(username, password); // 로그인 유저명과 비밀번호를 입력 합니다

       // 목록보기 구현

       FTPFile[] ftpfiles = ftpClient.listFiles("/public");  // public 폴더의 모든 파일을 list 합니다
       if (ftpfiles != null) {
           for (int i = 0; i < ftpfiles.length; i++) {
               FTPFile file = ftpfiles[i];
               System.out.println(file.toString());  // file.getName(), file.getSize() 등등..

           }
       }

       ftpClient.logout();
   }

} catch (Exception e) {
   System.out.println(e);
   e.printStackTrace();
} finally {
   if (ftpClient != null && ftpClient.isConnected()) {
    try {
         ftpClient.disconnect();
    } catch (IOException ioe) {
         ioe.printStackTrace();
    }
}


목록보기 방법에는 한가지 방법이 더 있습니다

위의 코드는 해당 디렉토리내의 모든 파일을 가져오는 방법이며 다음 방법은 해당 디렉토리의 모든 파일을 정해진 수로 짤라 가져오는 방법이 있습니다.

위의 빨간색 코드대신 다음 코드가 들어갈 수 있습니다.


    int page = 1;
    FTPListParseEngine engine = ftpClient.initiateListParsing("/public"); // 목록을 나타낼 디렉토리
    while (engine.hasNext()) {
        FTPFile[] ftpfiles = engine.getNext(10); // 10개 단위로 끊어서 가져온다


        System.out.println("------------------------ "+page+" ------------------------");    

        if (ftpfiles != null) {
            for (int i = 0; i < ftpfiles.length; i++) {
                 FTPFile file = ftpfiles[i];
                 System.out.println(file.toString());
           }
        }
        System.out.println("------------------------ "+(page++)+" -------------------------");
    }


V. 사용예제 - get (파일 다운로드)

서버의 /public/테스트.txt 파일을 클라이언트의 C:\\Test\\테스트.txt 에 다운받는 예제

    File get_file = new File("C:\\Test\\테스트.txt");  
    OutputStream outputStream = new FileOutputStream(get_file);
    boolean result = ftpClient.retrieveFile("/public/테스트.txt", outputStream);

    outputStream.close();


VI. 사용예제 - put(파일 업로드)

클라이언트의 C:\\Test\\보내자.txt 파일을 서버의 /public/보내자.txt 에 업로드하는 예제

만일 서버에 이미 /public/보내자.txt 파일이 있다면 덮어쓰게 된다

    File put_file = new File("C:\\Test\\보내자.txt");
    inputStream = new FileInputStream(put_file);
    boolean result = ftpClient.storeFile("/public/보내자.txt", inputStream);
    inputStream.close();


VII. 사용예제 - append (파일업로드)

위의 예제와 같은 기능을 하지만 이미 파일에 동일한 파일이 있으면 false를 반환하면서 실행하지 않는다

    File append_file = new File("C:\\Test\\더해라.txt");
    inputStream = new FileInputStream(append_file);
    boolean result = ftpClient.appendFile("/public/더해라.txt", inputStream);
    inputStream.close();


VIII. 사용예제 - rename (파일 이름변경)

변경전 파일명과 변경할 파일명을 파라미터로 준다

     boolean result = ftpClient.rename("/public/바꾸기전파일.txt", "/public/바꾼후파일.txt");


IX. 사용예제 - delete (파일삭제)

삭제할 파일을 선택한다    

     boolean result = ftpClient.deleteFile("/public/test.txt");


X. 사용예제 - directory 생성

/public 에 oops 디렉토리를 생성한다

     boolean result = ftpClient.makeDirectory("/public/oops");


XI. 사용예제 - OS 커맨드 입력하기

/public 에 oops 디렉토리를 생성한다

ftpClient.sendCommand(FTPCommand.MAKE_DIRECTORY, "/public/oops");


sendCommand 함수에 FTPCommand의 실행할 OS명령을 static 멤버변수를 넘겨준다


XII. 사용예제 - 작업디렉토리 설정하기

작업디렉토리를 /public 설정한 후 oops 디렉토리를 설정한다 (위의 예제와 비교해보라!)

ftpClient.changeWorkingDirectory("/public");

boolean result = ftpClient.makeDirectory("oops"); // /public/oops 절대경로를 적지 않아도 된다


XIII. 사용예제 - 파일 및 전송형태 설정

파일 형태 설정

ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

파일 타입

FTP.BINARY_FILE_TYPE, FTP.ASCII_FILE_TYPE, FTP.EBCDIC_FILE_TYPE, FTP.IMAGE_FILE_TYPE , FTP.LOCAL_FILE_TYPE

이 값을 설정하지 않으면 디폴트는 ASCII 이다


전송 형태 설정

ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);

전송 타입

FTP.BLOCK_TRANSFER_MODE, FTP.COMPRESSED_TRANSFER_MODE  

이값을 설정하지 않으면 디폴트는 FTP.STREAM_TRANSFER_MODE 이다



XIV. FTP접속시 글목록이 보이지 않는경우 아래 URL을 참고하세요


Jakarta Commons Net 에서 FTP 사용시 목록이 안보일 경우

http://www.jakartaproject.com/article/jakarta/1132032615162


목록이 안보일 경우 해결기 I탄

http://www.jakartaproject.com/article/jakarta/1135307924491


테스트한 파일을 첨부합니다 ^^


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================



Posted by 1010
반응형

Commons-Lang


VI. org.apache.commons.lang.builder.ToStringBuilder

toString()은 현 객체의 값을 문자열로 변환하여 반환 해주는 메소드로써 직접 구현할려면 노가다성 코드입니다.

하지만 Commons의 ToStringBuilder를 사용하면 는 말 그대로 클래스의 toString()을 쉽게 만들 수 있습니다.

웹에서는 빈클래스등에 toString을 구현하면 쉽게 디벙깅을 할 수 있습니다.



예제

ToStringTest.java

public class ToStringTest {


    public static void main(String args[]) {

        TestClass test = new TestClass();
        System.out.println(test.toString());
    }

}


TestClass.java

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;


public class TestClass {


    private String boardId;
    private int boardNo;


    public TestClass() {}


    public String toString() {
        return ToStringBuilder.reflectionToString(this);

    }


}


(1) refrectionToString 함수


public String toString() {
    return ToStringBuilder.reflectionToString(this);

}


reflectionToString은 static함수로써 위와같이 사용하며 파라미터로 스타일을 지정할 수 있습니다. 아래 코드를 봅시다

방법 ①

public String toString() {

    return ToStringBuilder.reflectionToString(this);

}


출력 결과

TestClass@1cd2e5f[boardId=<null>,boardNo=0]


방법 ②

public String toString() {

    return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE);

}


출력 결과

TestClass@1cd2e5f[boardId=<null>,boardNo=0]


방법 ③

public String toString() {

    return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);

}


출력 결과

TestClass@1cd2e5f[
  boardId=<null>
  boardNo=0
]


방법 ④

public String toString() {

    return ToStringBuilder.reflectionToString(this, ToStringStyle.NO_FIELD_NAMES_STYLE);

}


출력 결과

TestClass@1cd2e5f[<null>,0]


방법 ⑤

public String toString() {

    return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);

}


출력 결과

<null>,0


(2) append 함수

append 메소드는 static이 아니며 다음과 같은 형식으로 사용 가능합니다.


public String toString() {

    return new ToStringBuilder(this).append("boardId", boardId).append("boardNo", boardNo).toString();

}


출력 결과

TestClass@1cd2e5f[boardId=<null>,boardNo=0]


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Commons-Digester


I. Digester ?

digester는 XML파일로 저장된 정보를 java 객체에 매핑해 주는 API입니다. 하지만 그 반대기능은 되지 않습니다.

보통은 XML파일을 parsing할때 SAX와 DOM을 이용하였지만 DOM은 성능이 좀 느리고, SAX는 DOM보다는 빠르지만 코드가 난잡해 집니다. digester는 SAX기반이지만 pattern 매칭으로 아주 쉽고 빠르게 XML 파일을 parsing합니다


원래 digester는 struts의 struts-config.xml 정보를 로딩하기위해 개발되어 struts의 한 부분이었는데 독립적으로 commons project로 분리되었습니다.


II. 준비물

digester를 사용하기 위해서는 다음 4가지 준비물이 필요합니다

commons-digester http://jakarta.apache.org/site/downloads/downloads_commons-digester.cgi

commons-beanutils http://jakarta.apache.org/site/downloads/downloads_commons-beanutils.cgi

commons-collections http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi

commons-logging http://jakarta.apache.org/site/downloads/downloads_commons-logging.cgi

만약 digester는 SAX API를 사용하기 때문에 jdk1.4 이상의 버젼이 아니라면 Xerces같은 XML parser가 필요합니다


아래예제를 실행하려면 dbcp 관련 파일도 필요하며 이는 Commons-dbcp 편을 참조하세요~


참고 사이트

commons-digester API http://jakarta.apache.org/commons/digester/commons-digester-1.6/docs/api/index.html

commons-digester Guide http://jakarta.apache.org/commons/digester/apidocs/org/apache/commons/digester/package-summary.html


III. 시작하기 전에..

보통 digester는 여러 설정값들을 xml에 정의해 놓고 이를 어플리케이션에서 로드하는 방식으로 많이 이용됩니다. 이 강좌에서도 데이터베이스 정보를 xml에 정의해 놓고 이를 로딩하여 데이터베이스에 연결하는 예제를 강의할 것입니다.

시작하기 전에 XML에 대한 어느정도 기본 지식이 필요합니다.


주요함수

다른 함수들도 많이만 가장 많이 사용되는 다음 4가지만 딱 눈으로 익히고 갑시다


-. addObjectCreate(element 경로, 자바빈크래스) : 어떤 element 경로를 자바빈클래스로 매칭?

-. addSetProperties(element 속성명, 자바빈프로퍼티명) : 어떤 element 속성을 자바빈 변수에 설정?

-. addBeanPropertySetter(element 경로, 자바빈프로퍼티명) : 어떤 element 경로를 자바빈 변수에 설정?

-. addSetNext(element 경로, 자바빈함수) : 어떤 element 경로를 자바빈 함수에?

대강은 이런 뜻으로 알고 넘어 갑쉬다~!

그럼 element 경로가 먼가요? 다음에 나옵니다


Element Matching Pattern

XML에 element들의 path를 pattern으로 인식하는 방법만 익힙시다.


<a>                 -- Matches pattern "a"
    <b>             -- Matches pattern "a/b"
        <c/>        -- Matches pattern "a/b/c"
        <c/>        -- Matches pattern "a/b/c"
    </b>
    <b>             -- Matches pattern "a/b"
        <c/>        -- Matches pattern "a/b/c"
        <c/>        -- Matches pattern "a/b/c"
        <c/>        -- Matches pattern "a/b/c"
    </b>
</a>


위 XML을 보면 element a가 최상위 루트 element 입니다.

이것은 "a"로 매칭되며 그다음 a의 서브 element b는 "a/b" 로 매칭합니다

그다음은 .. "a/b/c".. 

쉽죠?

즉 최상위만 "/"가 붙지 않으며 그 이하는 트리구조처럼 "/"를 붙여주면 됩니다


자 그럼 좀전에 보았던 함수들과 연관지어 보면..

...

digester.addObjectCreate("a/b", B.class);

digester.addBeanPropertySetter("a/b/c", "c");

...

요렇게 쓰입니다.


IV. Digester를 이용하여 데이터베이스 커넥션 정보를 DBCP로 멀티 설정하여 웹에서 사용해 보자!

자 이제 실질적인 예제를 봅시다~


무엇을 하려고 하려면 mysql과 oracle jdbc정보를 xml 파일에 기록해 두고 이를 딱 한번만 읽어서 이정보를 데이터베이스 커넥션풀인 dbcp에 설정할 것입니다


 XML 파일

다음과 같은 XML 파일이 있습니다. 이 파일은 mysql과 oracle을 연결하는 커넥션 정보를 가지고 있습니다

이 파일이름은 C:\Tomcat 5.0\webapps\ROOT\WEB-INF\classes\config.xml입니다

mysql과 oracle 두개의 jdbc pool을 dbcp로 설정할 것입니다.

이 파일은 제가 임의로 정해서 만든겁니다.


<?xml version="1.0" encoding="EUC-KR"?>


<connection-sources>
   <description>This script is a connection description</description>


   <JDBCConnectionPool name="mysql">
      <description>Mysql Connection Source</description>
      <defaultAutoCommit>true</defaultAutoCommit>
      <defaultReadOnly>false</defaultReadOnly>
      <driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
      <maxActive>10</maxActive>
      <maxIdle>10</maxIdle>
      <maxWait>10000</maxWait>
      <username>unicorn</username>
      <password>iloveyou</password>
      <url>jdbc:mysql://localhost/unicorn</url>
   </JDBCConnectionPool>


   <JDBCConnectionPool name="oracle">
      <description>Oracle Connection Source</description>
      <defaultAutoCommit>true</defaultAutoCommit>
      <defaultReadOnly>false</defaultReadOnly>
      <driverClassName>oracle.jdbc.driver.OracleDriver</driverClassName>
      <maxActive>10</maxActive>
      <maxIdle>10</maxIdle>
      <maxWait>10000</maxWait>
      <username>unicorn</username>
      <password>iloveyoutoo</password>
      <url>jdbc:oracle:thin:@localhost:1521:unicorn</url>
   </JDBCConnectionPool>


</connection-sources>


web.xml

웹 배치파일에 의해 db.ConnectionInitialize.java를 초기 서블릿 컨테이너 로딩시 실행하여 XML정보를 DBCP로 세팅할 것입니다

config.xml 파일경로를 config 파라미터에 설정합니다


<?xml version="1.0" encoding="ISO-8859-1"?>

<!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>

  <servlet>
     <servlet-name>connectioninitialize</servlet-name>
     <servlet-class>db.ConnectionInitialize</servlet-class>
     <init-param>
        <param-name>config</param-name>
        <param-value>C:\Tomcat 5.0\webapps\ROOT\WEB-INF\classes\config.xml</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
  </servlet>
   
</web-app>


db.jdbc.ConnectionSources.java

XML 파일에서도 보면 알수있듯이 connection-sources 서브요소인 description과 JDBCConnectionPool을 저장하는 객체입니다. 변수를 눈여겨 봅시다


package db.jdbc;

import java.util.HashMap;

public class ConnectionSources {

	private String description;
	private HashMap source = new HashMap();
	
	public void setDescription(String description) {
		this.description = description;
	}
	
	public void addSource(JDBCConnectionPool source) {
		this.source.put(source.getName(), source);
	}
	
	public String getDescription() {
		return this.description;
	}
	
	public JDBCConnectionPool getSource(String name) {
		return (JDBCConnectionPool)this.source.get(name);
	}
}

addSource 함수는 여러 JDBCConnectionPool 정보를 ConnectionSourcec 의 source에 저장합니다. 이 함수는 밑에서 다시 나오니 눈여겨 봅시다

 

db.jdbc.JDBCConnectionPool.java

변수에 대해 단순히 setter, getter로 이루어져 있습니다.

XML 파일을 보면JDBCConnectionPool 의 서브 element들을 저장하는 객체이며 XML파일과  이 java 변수명들과 매칭되는것을 알수 있을겁니다.

대강 감이 오나요? ㅡ.ㅡ? 감좌봐쓰~?


package db.jdbc;

public class JDBCConnectionPool {

	private String name;
	private String description;
	private boolean defaultAutoCommit;
	private boolean defaultReadOnly;
	private String driverClassName;
	private int maxActive;
	private int maxIdle;
	private int maxWait;
	private String username;
	private String password;
	private String url;
	
//for debug public void print() { String toString = "name : "+name+"\n"+ "description : "+description+"\n"+ "defaultAutoCommit : "+defaultAutoCommit+"\n"+ "defaultReadOnly : "+defaultReadOnly+"\n"+ "driverClassName : "+driverClassName+"\n"+ "maxActive : "+maxActive+"\n"+ "maxIdle : "+maxIdle+"\n"+ "maxWait : "+maxWait+"\n"+ "username : "+username+"\n"+ "password : "+password+"\n"+ "url : "+url; System.out.println(toString); } public void setName(String name) { this.name = name; } public void setDescription(String description) { this.description = description; } public void setDefaultAutoCommit(boolean defaultAutoCommit) { this.defaultAutoCommit = defaultAutoCommit; } public void setDefaultReadOnly(boolean defaultReadOnly) { this.defaultReadOnly = defaultReadOnly; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public void setMaxWait(int maxWait) { this.maxWait = maxWait; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setUrl(String url) { this.url = url; } public String getName() { return this.name; } public String getDescription() { return this.description; } public boolean getDefaultAutoCommit() { return this.defaultAutoCommit; } public boolean getDefaultReadOnly() { return this.defaultReadOnly; } public String getDriverClassName() { return this.driverClassName; } public int getMaxActive() { return this.maxActive; } public int getMaxIdle() { return this.maxIdle; } public int getMaxWait() { return this.maxWait; } public String getUsername() { return this.username; } public String getPassword() { return this.password; } public String getUrl() { return this.url; } }

db.ConnectionInitialize.java

자 이제 여기가 핵심 클래스입니다. 위부분이 모두이해가 되었으면 다음 소스코드를 살펴봅시다

web.xml에서 정의한 config.xml을 로딩하여 파싱하고 그 정보를 DBCP에 설정합니다

mysql과 oracle 두개의 jdbc를 설정하도록 xml에 정의하였었습니다.


package db;

import java.sql.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;

import org.apache.commons.digester.Digester;
import java.io.*;
import org.xml.sax.SAXException;

import db.jdbc.ConnectionSources;
import db.jdbc.JDBCConnectionPool;

public class ConnectionInitialize extends HttpServlet {

	public void init() throws ServletException {

		String config = null;
		// web.xml의 배치스크립트의 파라미터이름을 가져온다
		Enumeration names = getServletConfig().getInitParameterNames();
		do {
			if(!names.hasMoreElements())
			break;
			String name = (String)names.nextElement();
			String value = getServletConfig().getInitParameter(name).trim();

			System.out.println(name+" : "+value);

			// config에 정의된 XML 파일을 가져온다
			if (name.startsWith("config"))
 				config = value;
		} while(true);

		//Digester를 생성하고
		Digester digester = new Digester();
		//XML 유효성을 검증할것인가?
		digester.setValidating(false);

		//connection-sources 요소를 ConnectionSources.class 객체에 저장하겠다
		digester.addObjectCreate("connection-sources",
		                          ConnectionSources.class);

		//connection-sources/description 요소를
		//ConnectionSources.class의 description 변수에 저장하겠다
		digester.addBeanPropertySetter("connection-sources/description",
						"description");

		//connection-sources/JDBCConnectionPool 요소를 JDBCConnectionPool.class 객체에저장하겠다
		digester.addObjectCreate("connection-sources/JDBCConnectionPool",
		                         db.jdbc.JDBCConnectionPool.class);

		//connection-sources/JDBCConnectionPool 요소의 name 속성을
		//JDBCConnectionPool.class의 name 변수에 저장하겠다
		digester.addSetProperties("connection-sources/JDBCConnectionPool", "name", "name");

		//connection-sources/JDBCConnectionPool/description 을
		//JDBCConnectionPool.class 객에의 description 변수에 저장하겠다
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/description",
                                       "description");

		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/defaultAutoCommit",
                                       "defaultAutoCommit");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/defaultReadOnly",
                                       "defaultReadOnly");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/driverClassName",
                                       "driverClassName");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/maxActive",
                                       "maxActive");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/maxIdle",
                                       "maxIdle");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/maxWait",
                                       "maxWait");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/username",
                                       "username");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/password",
                                       "password");
		digester.addBeanPropertySetter("connection-sources/JDBCConnectionPool/url", "url");

		//connection-sources/JDBCConnectionPool을
		//ConnectionResources.class의 addSource를 이용하여 반복적으로 추가하겠다
		digester.addSetNext("connection-sources/JDBCConnectionPool", "addSource");

		try {
			File file = new File(config);
			//digester 파싱 및 결과 리턴
			ConnectionSources connectionSources =
                                                             (ConnectionSources)digester.parse(file);

			System.out.println(connectionSources.getDescription());
			JDBCConnectionPool mysql = connectionSources.getSource("mysql");
			mysql.print(); //디버깅
			JDBCConnectionPool oracle = connectionSources.getSource("oracle");
			oracle.print(); //디버깅

			//이부분 이하는 Commons-dbcp 부분을 참조하기 바란다
			setupDriver(mysql.getName(),
				mysql.getDriverClassName(),
				mysql.getUrl(),
				mysql.getUsername(),
				mysql.getPassword(),
				mysql.getDefaultAutoCommit(),
				mysql.getDefaultReadOnly(),
				mysql.getMaxActive(),
				mysql.getMaxIdle(),
				mysql.getMaxWait());

			setupDriver(oracle.getName(),
				oracle.getDriverClassName(),
				oracle.getUrl(),
				oracle.getUsername(),
				oracle.getPassword(),
				oracle.getDefaultAutoCommit(),
				oracle.getDefaultReadOnly(),
				oracle.getMaxActive(),
				oracle.getMaxIdle(),
				oracle.getMaxWait());

		} catch (FileNotFoundException filenotfoundexception) {
			System.out.println("Config file not found");
			filenotfoundexception.printStackTrace();
		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}

	public void setupDriver(String poolName,
			String driverClassName,
			String url,
			String username,
			String password,
			boolean defaultAutoCommit,
			boolean defaultReadOnly,
			int maxActive,
			int maxIdle,
			long maxWait) throws Exception {
		try {
		    Class.forName(driverClassName);
		} catch (ClassNotFoundException classnotfoundexception) {
			System.out.println(driverClassName+" is not found");
		    classnotfoundexception.printStackTrace();
		    throw classnotfoundexception;
		}
		
		GenericObjectPool connectionPool = new GenericObjectPool(null);
		connectionPool.setMaxActive(maxActive);
		connectionPool.setMaxIdle(maxIdle);
		connectionPool.setMaxWait(maxWait);
		
		ConnectionFactory connectionFactory
			= new DriverManagerConnectionFactory(url, username, password);
		
		PoolableConnectionFactory poolableConnectionFactory
			= new PoolableConnectionFactory(connectionFactory,
		                                   connectionPool,
		                                   null,
		                                   null,
		                                   defaultReadOnly,
		                                   defaultAutoCommit);
		
		Class.forName("org.apache.commons.dbcp.PoolingDriver");
		PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
		
		driver.registerPool(poolName,connectionPool);    
	}
}

그럼 이렇게 설정한 DBCP는 어떻게 사용할까요?

이전강좌에서 보았던 ConnectionContext.java와 ConnectionResource.java를 다시 사용해봅시다

package db;

public interface ConnectionContext {
	public java.sql.Connection getConnection();
	public void rollback();
	public void commit();
	public void release();
}

ConectionContext를 구현한 ConnectionResource는 풀이름을 받아 처리하는것으로 수정하였습니다

package db;

import java.sql.Connection;
import java.sql.DriverManager;
import javax.sql.DataSource;

public class ConnectionResource implements ConnectionContext {
	private Connection connection = null;
	private boolean transaction = false;

    public ConnectionResource(String poolName) throws Exception {
    	init(false, poolName);	
    }
    
    public ConnectionResource(boolean transaction, String poolName) throws Exception {
    	init(transaction, poolName);
    }
    
    public void init(boolean transaction, String poolName) throws Exception {
    	this.transaction = transaction;
    	connection = DriverManager.getConnection("jdbc:apache:commons:dbcp:"+poolName);
if (transaction) connection.setAutoCommit(false); if (connection == null) throw new Exception("fail to get connection"); } public Connection getConnection() { return connection; } public void rollback() { if (transaction) { if (connection != null) try { connection.rollback(); } catch (Exception e) {} } } public void commit() { if (transaction) { if (connection != null) try { connection.commit(); } catch (Exception e) {} } } public void release() { { if (connection != null) { if (transaction) { try { connection.setAutoCommit(true); } catch (Exception e) {} } try { connection.close(); } catch (Exception e) {} }    } }

jsp 파일

마지막으로 ConnectionContext는 풀이름과 함께 사용하면 되겠네요

<%@ page contentType="text/html;charset=EUC_KR" %>
<%@ page import="java.sql.*,db.*" %>

<%

ConnectionContext mysqlContext = new ConnectionResource("mysql");
// 혹은 ConnectionContext oracleContext = new ConnectionResource("oracle");


Connection connection = null;


try {


    connection = mysqlContext.getConnection();
    out.println(connection);


} catch (Exception exception) {
    exception.printStackTrace();
} finally {
    if (connection != null) try { mysqlContext.release(); } catch (Exception exception) {}
}

%>


DBCP 부분은 이전 강좌인 Commons-dbcp (http://www.jakartaproject.com/article/jakarta/1111890409958 )부분을 참조하세요~


ps. 휴 =3 다썼당 ^_^

     흠.. 쉽게쓴다고 이리 저리 하루종일 썼는데 설명이 많이 부족한것 같네요

     그밖에 사항은 알아서 찾아보기~!

     digester II탄에서는 xml 설정파일로부터 규칙을 읽어 xml을 파싱하는 방법을 알아보겠습니다~


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

Posted by 1010
반응형

Commons-dbcp


 

I. DBCP (DataBase Connection Pool)

이제는 데이터베이스 풀을 이용한 커넥션 풀을 사용하지 않는다는것은 상상조차 할 수 없게 되었다. 각 WAS의 벤더들은 오래전부터 자사제품에 대해 최적화된 커넥션 풀을 기본적으로 제공을 하기까지 이르렀다. 또한 한스버그의 커넥션 풀링이나 풀맨의 풀등 여러 오픈된 커넥션 풀 소스들이 돌아다기기도 하였다.

여기서는 Jakarta Commons에서 진행하고있는 Commons-dbcp project에 대해 알아보고 간단한 예제를 소개하기로 하겠다


II. 다운로드 및 설치

dbcp는 commons의 pool과 collection을 사용하기때문에 다음3가지를 모두 설치해야 정상적으로 dbcp를 사용할 수 있다.

Commons-dbcp http://jakarta.apache.org/site/downloads/downloads_commons-dbcp.cgi

Commons-pool http://jakarta.apache.org/site/downloads/downloads_commons-pool.cgi

Commons-collections http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi

DBCP API http://jakarta.apache.org/commons/dbcp/apidocs/index.html


/WEB-INF/lib/ 에 다운받은 3가지 *.jar를 복사하도록 한다


III. DBCP 웹에서 사용하기!

이번 dbcp 강의는 웹에서 사용하는것을 전제로 설명하겠습니다 ^^

톰캣을 사용한다면 server.xml에 dbcp를 설정해도 되지만 web.xml에 설정하는것이 좀더 유연성을 지닙니다


1) JDBC 정보 정의

web.xml에 dbcp를 사용하기위한 jdbc 정보를 정의합니다.

   <servlet>
      <servlet-name>connectioninitialize</servlet-name>
      <servlet-class>db.ConnectionInitialize</servlet-class>
      <init-param>
         <param-name>driverClassName</param-name>
         <param-value>org.gjt.mm.mysql.Driver</param-value>
      </init-param>
      <init-param>
      <param-name>url</param-name>
      <param-value>jdbc:mysql://localhost/unicorn</param-value>
      </init-param>
      <init-param>
         <param-name>username</param-name>
         <param-value>unicorn</param-value>
      </init-param>
      <init-param>
         <param-name>password</param-name>
         <param-value>iloveyou</param-value>
      </init-param>
      <init-param>
         <param-name>defaultAutoCommit</param-name>
         <param-value>true</param-value>
      </init-param>
      <init-param>
         <param-name>defaultReadOnly</param-name>
         <param-value>false</param-value>
      </init-param>
      <init-param>
         <param-name>maxActive</param-name>
         <param-value>30</param-value>
      </init-param>
      <init-param>
         <param-name>maxIdle</param-name>
         <param-value>10</param-value>
      </init-param>
      <init-param>
         <param-name>maxWait</param-name>
         <param-value>10000</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>   
   </servlet>


driverClassName : 사용할 jdbc driver의 class name

url : jdbc driver의해 연결할 connection에 대한 url

username : connection을 연결할 사용자 이름

password : connection을 연결할 사용자 비밀번호

defaultAutoCommit : 풀에의해 생성된 커넥션의 auto-commit 상태

defaultReadOnly : 풀에의해 생성된 커넥션의 read-only 상태

                             jdbc에 따라 지원하지 않을수도 있다 (예 : informix)

maxActive : 커넥션풀의 Active한 커넥션의 최대 갯수

maxIdle : 커넥션풀의 idle한 커넥션의 최대 갯수

maxWait : 커넥션이 풀에 반납되기까지 기다리는 최대 시간 (millisecond)


2) JDBC 정보 로딩 및 초기화

이 클래스는 web.xml에 의해 서블릿 컨테이너가 구동될 때 딱 한번 실행됩니다.

db.ConnectionInitialize.java

package db;


import java.sql.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;


public class ConnectionInitialize extends HttpServlet {
 
 public void init() throws ServletException {
 
    String driverClassName = null;
    String url = null;
    String username = null;
    String password = null;
    boolean defaultAutoCommit = true;
    boolean defaultReadOnly = false;
    int maxActive = 0;
    int maxIdle = 0;
    long maxWait = 0;


    // web.xml에서 정의한 파라미터 이름을 enumeration에 담습니다.

    Enumeration names = getServletConfig().getInitParameterNames();
       do {
          if(!names.hasMoreElements())
             break;


          // web.xml에 정의한 파라미터값을 가져옵니다.

          String name = (String)names.nextElement();
          String value = getServletConfig().getInitParameter(name).trim();

          System.out.println(name+" : "+value);


          if (name.startsWith("driverClassName"))
             driverClassName = value;
          else if (name.startsWith("url"))
             url = value;
          else if (name.startsWith("username"))
             username = value;
          else if (name.startsWith("password"))
             password = value;
          else if (name.startsWith("defaultAutoCommit"))
             defaultAutoCommit = "true".equals(value);
          else if (name.startsWith("defaultReadOnly"))
             defaultReadOnly = "true".equals(value);
          else if (name.startsWith("maxActive"))
             maxActive = Integer.parseInt(value);
          else if (name.startsWith("maxIdle"))
             maxIdle = Integer.parseInt(value);
          else if (name.startsWith("maxWait"))
             maxWait = Long.parseLong(value);

        } while(true);
       
        try {

           // jdbc driver 및 기타 정의를 설정합니다.
           setupDriver(driverClassName,

                           url,

                           username,

                           password,

                           defaultAutoCommit,

                           defaultReadOnly,

                           maxActive,

                           maxIdle,

                           maxWait);


           System.out.println("Connection initialize success");
        } catch (Exception exception) {
           System.out.println("Connection initialize fail!");
           exception.printStackTrace();
      }
 }
 
 public void setupDriver(String driverClassName,
       String url,
       String username,
       String password,
       boolean defaultAutoCommit,
       boolean defaultReadOnly,
       int maxActive,
       int maxIdle,
       long maxWait) throws Exception {


       try {

            // jdbc class를 로딩합니다.
            Class.forName(driverClassName);
        } catch (ClassNotFoundException classnotfoundexception) {
            System.out.println(driverClassName+" is not found");
            classnotfoundexception.printStackTrace();
            throw classnotfoundexception;
        }
       

        // 커넥션 풀로 사용할 commons-collections의 genericOjbectPool을 생성합니다.
        GenericObjectPool connectionPool = new GenericObjectPool(null);
        connectionPool.setMaxActive(maxActive);
        connectionPool.setMaxIdle(maxIdle);
        connectionPool.setMaxWait(maxWait);
       

        // 풀이 커넥션을 생성하는데 사용하는 DriverManagerConnectionFactory를 생성합니다.
        ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(url, username, password);
       

        // ConnectionFactory의 래퍼 클래스인 PoolableConnectionFactory를 생성한다
        PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, connectionPool, null, null, defaultReadOnly, defaultAutoCommit);
       

        // 마지막으로 PoolingDriver 자신을 로딩한다
        Class.forName("org.apache.commons.dbcp.PoolingDriver");

        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");

        // 그리고 풀에 등록한다. 풀이름을 "unicorn"이라고 지정하였다        
        driver.registerPool("unicorn",connectionPool);       
    }
}



3) 커넥션 래퍼 클래스

데이터베이스 커넥션을 관리하는 Interface로 다음 4가지 함수를 정의하였습니다.

db.ConnectionContext.java

package db;


public interface ConnectionContext {
   public java.sql.Connection getConnection();
   public void rollback();
   public void commit();
   public void release();
}


ConectionContext를 구현한 커넥션 클래스

db.ConnectionResource.java

package db;

import java.sql.Connection;
import java.sql.DriverManager;

public class ConnectionResource implements ConnectionContext {
   private Connection connection = null;
   private boolean transaction = false;


   public ConnectionResource() throws Exception {
      init(false);
   }
   
    public ConnectionResource(boolean transaction) throws Exception {
       init(transaction);
    }
   
    public void init(boolean transaction) throws Exception {
       this.transaction = transaction;
       connection = DriverManager.getConnection("jdbc:apache:commons:dbcp:unicorn");

       if (connection == null) throw new Exception("fail to get connection");

      if (transaction) connection.setAutoCommit(false);

    }
     
    public Connection getConnection() {  
       return connection;
    }


    public void rollback() {
       if (transaction) {
          if (connection != null) try { connection.rollback(); } catch (Exception e) {}
      }
   }


   public void commit() {
      if (transaction) {
         if (connection != null) try { connection.commit(); } catch (Exception e) {}
      }
   }


   public void release() {
      if (connection != null) {
         if (transaction) {
            try { connection.setAutoCommit(true); } catch (Exception e) {}
         }
         try { connection.close(); } catch (Exception e) {}
      }
   }
}


4) 테스트 JSP

jsp에서 다음과 같이 사용하면 됩니다.

<%@ page contentType="text/html;charset=EUC_KR" %>
<%@ page import="java.sql.*,db.*" %>


<%

   ConnectionContext connectionContext = new ConnectionResource();
   Connection connection = null;

   try {
      connection = connectionContext.getConnection();

   } catch (Exception exception) {
       exception.printStackTrace();
   } finally {
       if (connection != null) try { connectionContext.release(); } catch (Exception exception) {}
   }

%>

관련 파일들을 첨부합니다 ^^&


IV. Struts와 DBCP

만약 Struts와 함께 사용한다면 다음과 같이 사용하세요. 현재 Unicorn이 다음과 같은 형식으로 되어있습니다.

1) struts-config.xml 에 DBCP 정보를 정의합니다.

 <data-sources>
  <data-source key="mysql" type="org.apache.commons.dbcp.BasicDataSource">
   <set-property property="defaultAutoCommit" value="true"/>
   <set-property property="defaultReadOnly" value="false"/>
   <set-property property="description" value="Tomcat Data Source"/>
   <set-property property="driverClassName" value="org.gjt.mm.mysql.Driver"/>
   <set-property property="maxActive" value="30"/>
   <set-property property="maxIdle" value="10"/>
   <set-property property="maxWait" value="10000"/>
   <set-property property="username" value="unicorn"/>
   <set-property property="password" value="iloveyou"/>
   <set-property property="url" value="jdbc:mysql://localhost/unicorn"/>
  </data-source>
 </data-sources>


2) Action classs에서 다음과 같이 커넥션을 얻어올 수 있습니다.

Connection connection = getDataSource(request, "mysql").getConnection();

이 getDataSource() 함수는 org.apache.struts.action.Action 클래스에 다음과 같이 정의되어 있습니다.

protected DataSource getDataSource(HttpServletRequest request) {
    return getDataSource(request, "org.apache.struts.action.DATA_SOURCE");
}

protected DataSource getDataSource(HttpServletRequest request, String key) {
    ServletContext context = getServlet().getServletContext();
    ModuleConfig moduleConfig = ModuleUtils.getInstance().getModuleConfig(request, context);
    return (DataSource)context.getAttribute(key + moduleConfig.getPrefix());
}


3) Struts에서 트랜잭션 관리를 위한 코딩으로는 unicorn의 소스에 충실히 구현되어 있습니다.

마찬가지로 Interface인 ConnectionContext와 이를 구현한 ConnectionResource를 사용하여 트랜잭션 관리를 하였습니다. 이 예제는 unicorn 소스를 참조하세요 ^^


V. 톰캣에서의 DBCP 사용

1) DBCP에 대한 정보를 server.xml에 정의할 수 있습니다.

<Context path="/unicorn" docBase="unicorn" debug="0"
                 reloadable="true" crossContext="true">
          <Logger className="org.apache.catalina.logger.FileLogger"
                     prefix="localhost_ktng_log." suffix=".txt"
              timestamp="true"/>


    <Resource name="jdbc/unicorn" auth="Container"

                    type="javax.sql.DataSource"/>
    <ResourceParams name="jdbc/unicorn">
       <parameter>
         <name>factory</name>
         <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
       </parameter>
       <parameter>
         <name>username</name>
         <value>unicorn</value>
       </parameter>
       <parameter>
         <name>password</name>
         <value>iloveyou</value>
       </parameter>
       <parameter>
         <name>driverClassName</name>
         <value>org.gjt.mm.mysql.Driver</value>
       </parameter>
       <parameter>
         <name>url</name>
         <value>jdbc:mysql://localhost/unicorn</value>
       </parameter>
       <parameter>
         <name>maxActive</name>
         <value>30</value>
       </parameter>
       <parameter>
         <name>maxIdle</name>
         <value>10</value>
       </parameter>
       <parameter>
         <name>maxWait</name>
         <value>10000</value>
       </parameter>
     </ResourceParams>


2) web.xml에 JNDI 리소스를 사용할 수 있도록 다음을 추가합니다.

<resource-ref>
   <description>unicorn db</description>
   <res-ref-name>jdbc/unicorn</res-ref-name>
   <res-type>javax.sql.DataSource</res-type>
   <res-auth>Container</res-auth>
</resource-ref>

3) 실제 소스에서는 다음과 같이 DataSource를 사용합니다.

InitialContext context = new InitialContext();

DataSource dataSource = (DataSource) context.lookup("java:/comp/env/jdbc/unicorn");

Connection connection = dataSource.getConnection();

간단하죵?


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Commons-Lang


 

V. org.apache.commons.lang.StringEscapeUtils


이 유틸또한 org.apache.commons.lang.StringUtils 처럼 String처리에 대한 유틸입니다.

하지만 기능적인면을 좀더 부각시켜 놓은듯한 유틸입니다.

즉 HTML을 다음과 같이 <는 &lt;로, >는 &gt;로 나타내어야 하는데 이것역시 StringUtils만으론 부족합니다. replace를 사용하면 되지만 여러번 되겠죠.

이런경우 StringExcapeUtils 에 escapeHtml 함수를 사용하면 됩니다.


이와같이 Html이나 Xml, Javascript, SQL등을 처리하는데 필요한 StringUtils입니다


반환값 함수명 파라미터
String escapeHtml String str
str을 html 포맷으로 변환시킨다 즉 <는 &lt;, >는 &gt; 등으로 변환시킨다
String escapeJava String str
str의 특정문자를 \b, \n, \t, \f, \r등 자바특수문자로 변환시킨다 또한 "는 \"로, \는 \\로 변환시킨다
void escapeJava Writer out,
String str escapeJava를 out에 출력한다
String escapeJavaScript String str
escapeJava와 동일하며 '는 \' 로 변환하는것이 추가되어있다
void escapeJavaScript Writer out, String str
escapeJavaScript와 동일하며 out에 출력한다
String escapeSql String str
str에 '를 ''로 변환시킨다
String escapeXml String str
str을 XML 포맷으로 변환시킨다
String unescapeHtml String str
escapeHtml 메소드와 역기능
String unescapeJava String str
escapeJava 메소드와 역기능
void unescapeJava Writer out, String str
escapeJava 메소드와 역기능
String unescapeJavaScript String str
escapeJavaScript 메소드와 역기능
void unescapeJavaScript Writer out, String str
escapeJavaScript 메소드와 역기능
String unescapeXml String str
escapeXml 메소드와 역기능

사용법

모두 static 함수이기때문에 다음과 같이 사용하면 됩니다.

String text = StringEscapeUtils.escapeHtml(str);


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Jakarta POI



IX. Cell을 좀더 유연하게!


1. Date타입 셀 만들기

소스

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");


HSSFRow row = sheet.createRow((short)0);


//처음셀은 style없이 그냥 new Date()로 입력

HSSFCell cell = row.createCell((short)0);
cell.setCellValue(new Date());


//두번째 셀은 "m/d/yy h:mm"으로 포맷하여 날짜를 입력

HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
cell = row.createCell((short)1);
cell.setCellValue(new Date());
cell.setCellStyle(cellStyle);


FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();


결과






HSSFDateFormat이 지원하는 날짜 포맷

cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm")); 에서 다음과 같이 포맷을 정할 수 있다 (현재시간은 2005년 3월 14일 0시 52분 17초.. 헛 화이트데이 --;)




2. Cell의 Align속성

소스

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short) 2);


//HSSFCellStyle의 여러가지 align속성
createCell(wb, row, (short) 0, HSSFCellStyle.ALIGN_CENTER);
createCell(wb, row, (short) 1, HSSFCellStyle.ALIGN_CENTER_SELECTION);
createCell(wb, row, (short) 2, HSSFCellStyle.ALIGN_FILL);
createCell(wb, row, (short) 3, HSSFCellStyle.ALIGN_GENERAL);
createCell(wb, row, (short) 4, HSSFCellStyle.ALIGN_JUSTIFY);
createCell(wb, row, (short) 5, HSSFCellStyle.ALIGN_LEFT);
createCell(wb, row, (short) 6, HSSFCellStyle.ALIGN_RIGHT);

FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();


 

public void createCell(HSSFWorkbook wb, HSSFRow row, short column, short align)
{
    HSSFCell cell = row.createCell(column);
    cell.setCellValue("Align It");
    HSSFCellStyle cellStyle = wb.createCellStyle();
    cellStyle.setAlignment(align);
    cell.setCellStyle(cellStyle);
}


결과








3. Cell의 Border 속성

소스

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");

HSSFRow row = sheet.createRow((short) 1);
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue(4);

// Cell의 Border 속성
HSSFCellStyle style = wb.createCellStyle();
style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style.setBottomBorderColor(HSSFColor.BLACK.index);
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style.setLeftBorderColor(HSSFColor.GREEN.index);
style.setBorderRight(HSSFCellStyle.BORDER_THIN);
style.setRightBorderColor(HSSFColor.BLUE.index);
style.setBorderTop(HSSFCellStyle.BORDER_MEDIUM_DASHED);
style.setTopBorderColor(HSSFColor.BLACK.index);
cell.setCellStyle(style);

FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();


결과








HSSFCellStyle

HSSFCellStyle에는 다음과 같은 static 멤버변수가 존재합니다

ALIGN_CENTER center horizontal alignment
ALIGN_CENTER_SELECTION center-selection? horizontal alignment
ALIGN_FILL fill? horizontal alignment
ALIGN_GENERAL general (normal) horizontal alignment
ALIGN_JUSTIFY justified horizontal alignment
ALIGN_LEFT left-justified horizontal alignment
ALIGN_RIGHT right-justified horizontal alignment
ALT_BARS Wide dots
BIG_SPOTS Large spots
BORDER_DASH_DOT dash-dot border
BORDER_DASH_DOT_DOT dash-dot-dot border
BORDER_DASHED dash border
BORDER_DOTTED hair-line border
BORDER_DOUBLE double-line border
BORDER_HAIR dot border
BORDER_MEDIUM Medium border
BORDER_MEDIUM_DASH_DOT medium dash-dot border
BORDER_MEDIUM_DASH_DOT_DOT medium dash-dot-dot border
BORDER_MEDIUM_DASHED Medium dashed border
BORDER_NONE No border
BORDER_SLANTED_DASH_DOT slanted dash-dot border
BORDER_THICK Thick border
BORDER_THIN Thin border
BRICKS Brick-like layout
DIAMONDS Diamonds
FINE_DOTS Small fine dots
NO_FILL No background
SOLID_FOREGROUND Solidly filled
SPARSE_DOTS Sparse dots
SQUARES Squares
THICK_BACKWARD_DIAG Thick backward facing diagonals
THICK_FORWARD_DIAG Thick forward facing diagonals
THICK_HORZ_BANDS Thick horizontal bands
THICK_VERT_BANDS Thick vertical bands
THIN_BACKWARD_DIAG Thin backward diagonal
THIN_FORWARD_DIAG Thin forward diagonal
THIN_HORZ_BANDS Thin horizontal bands
THIN_VERT_BANDS Thin vertical bands
VERTICAL_BOTTOM bottom-aligned vertical alignment
VERTICAL_CENTER center-aligned vertical alignment
VERTICAL_JUSTIFY vertically justified vertical alignment
VERTICAL_TOP top-aligned vertical alignment


4. Cell의 색갈 채우기

소스

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");

HSSFRow row = sheet.createRow((short) 1);


HSSFCellStyle style = wb.createCellStyle();
// 아쿠아색을 배경으로 하고

style.setFillBackgroundColor(HSSFColor.AQUA.index);

//채움 스타일은 큰 점으로 한다
style.setFillPattern(HSSFCellStyle.BIG_SPOTS);
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue("X");
cell.setCellStyle(style);


style = wb.createCellStyle();

// 오렌지색으로 전경으로 하고
style.setFillForegroundColor(HSSFColor.ORANGE.index);

// 채움 스타일은 SOLID_FOREGROUND로 한다
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
cell = row.createCell((short) 2);
cell.setCellValue("X");
cell.setCellStyle(style);


FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();


결과







HSSFColor 정리!





5. Cell 병합

소스

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");

HSSFRow row = sheet.createRow((short) 1);
HSSFCell cell = row.createCell((short) 1);

cell.setCellValue("This is a test of merging");

sheet.addMergedRegion(new Region(1,(short)1,1,(short)2));


FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();


결과








Region

특정셀을 합칠 때는 HSSFSheet의 addMergedRegion(Region region)와

합칠 셀의 영역을 나타내는 Region을 사용한다.

Region region = new (int 시작ROW, short 시작COL, int 종료ROW, short 종료COL);



6. Cell에 폰트 설정하기

소스

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");

HSSFRow row = sheet.createRow((short) 1);


// 폰트 높이는 24, 폰트 종류는 Courier New, 이탈릭체로 설정한다
HSSFFont font = wb.createFont();
font.setFontHeightInPoints((short)24);
font.setFontName("Courier New");
font.setItalic(true);
font.setStrikeout(true);


// 설정한 폰트를 스타일에 적용한다
HSSFCellStyle style = wb.createCellStyle();
style.setFont(font);


// Cell에 스타일을 적용한다
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue("This is a test of fonts");
cell.setCellStyle(style);


FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();


결과








=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Commons-Lang



IV. org.apache.commons.lang.StringUtils


java api의 String을 보면 문자열 처리하기에는 제공하는 기능들이 너무 빈약합니다.

그래서 프로그래머들은 자기 나름데로의 독특한 Utils를 만들어 사용하지요.

하지만 Commons-lang에서 제공하는 StringUtils를 사용한다면 더이상 자신만의 Utils를 사용할 필요가 없습니다.

여기 모두 ㄷ ㅏ ~ 있기 때문이지요 ^^

한가지 더 장점이 있다면 String의 null 값때문에 더이상 NullPointerException을 생각하지 않아도 된다는 것입니다.

StringUtils에서 ㄷ ㅏ ~ 알아서 해주기 때문입니다.


그럼 어떠한 기능들의 함수가 있는지 알아봅시다


반환값 함수명 파라미터
String abbreviate String str, int maxWidth
"…"를 포함한 지정한 사이즈로 문자열을 축소한다 maxWidth은 4보다 커야한다
String abbreviate String str, int offset, int maxWidth
"…"를 포함한 지정한 사이즈로 문자열을 앞/뒤로 축소한다 offset은 7보다 커야한다
String capitalise String str
deprecated
String capitaliseAllWords String str
deprecated
String capitalize String str
문자열중 첫번째 문자를 대문자로 만든다
String center String str, int size
str의 길이가 size가 되도록 문자열 좌우에 공백문자를 하나씩 추가한다 (우측부터 시작한다)
String center String str, int size, char padChar
str의 길이가 size가 되도록 문자열 좌우에 padChar를 하나씩 추가한다 (우측부터 시작한다)
String center String str, int size, String padStr
str의 길이가 size가 되도록 문자열 좌우에 padStr을 하나씩 추가한다 (우측부터 시작한다)
String chomp String str
문자열 맨 끝에있는 '\n' , '\r', '\r\n'을 제거한다
String chomp String str, String separator
문자열 맨 끝에 separator가 있으면 이를 제거한다
String chompLast String str
deprecated
String chompLast String str, String sep
deprecated
String chop String str
문자열 맨 끝에있는 문자 하나를 제거한다
String chopNewline String str
deprecated
String clean String str
deprecated
String concatenate Object array[]
deprecated
boolean contains String str, char searchChar
str이 searchChar를 포함하고 있으면 true
boolean contains String str, String searchStr
str이 searchStr을 포함하고 있으면 true
boolean containsNone String str, char invalidChars[]
str이 invalidChars의 각 문자를 모두 포함하고 있지 않으면 true
boolean containsNone String str, String invalidChars
str이 invalidChars를 모두 포함하고 있지 않으면 true
boolean containsOnly String str, char valid[]
str이 valid의 각 문자들만 포함하고 있어야 true
boolean containsOnly String str, String validChars
str이 validChars 들만을 포함하고 있어야 true
int countMatches String str, String sub
str중에 sub가 포함되어있는 갯수를 반환한다
String defaultString String str
str이 null이면 ""를 반환하고 아니면 str을 반환한다
String defaultString String str, String defaultStr
str이 null이면 defaultStr을 반환하고 아니면 str을 반환한다
String deleteSpaces String str
deprecated
String deleteWhitespace String str
문자열중 공백문자가 있으면 모두 제거한다
String difference String str1, String str2
str1과 str2를 비교하여 다른부분을 반환한다 (str2의 부분을 반환)
boolean equals String str1, String str2
str1이 null이면 str2가 null인지 유무를 반환하고 str1이 null이 아니면 str1과 str2의 equals를 반환
boolean equalsIgnoreCase String str1, String str2
equals와 동일하며 대소문자를 무시하고 비교한다
String escape String str
deprecated
String getChomp String str, String sep
deprecated
String getNestedString String str, String tag
deprecated
String getNestedString String str, String open, String close
deprecated
String getPrechomp String str, String sep
deprecated
int indexOf String str, char searchChar
str에서 첫번째 searchChar의 인덱스를 반환한다
int indexOf String str, char searchChar, int startPos
str의 startPos 인덱스부터 첫번째 searchChar의 인덱스를 반환한다
int indexOf String str, String searchStr
str에서 첫번째 searchStr의 인덱스를 반환한다
int indexOf String str, String searchStr, int startPos
str의 startPos 인덱스로부터 첫번째 searchStr의 인덱스를 반환한다
int indexOfAny String str, char searchChars[]
str에서 searchChars중 포함하고 있는 문자의 첫번째 인덱스를 반환한다
int indexOfAny String str String searchChars
str에서 searchChars중 포함하고 있는 문자열의 첫번째 인덱스를 반환한다
int indexOfAny String str, String searchStrs[]
str에서 searchStr중 포함하고 잇는 문자열의 첫번째 인덱스를 반환한다
int indexOfAnyBut String str char searchChars[]
str에서 searchChars중 포함되지 않은 문자 첫번째 인덱스를 반환한다
int indexOfAnyBut String str, String searchChars
str에서 searchChars중 포함되지 않은 문자 첫번째 인덱스를 반환한다
int indexOfDifference String str1, String str2
str1과 str2를 비교하여 문자열이 틀려지기 시작하는 인덱스를 반환한다
boolean isAlpha String str
문자열이 모두 Character.isLetter 이면 true (모두 문자이면 true)
boolean isAlphanumeric String str
문자열이 모두 Character.isLetterOrDigit 이면 true (문자거나 숫자이면 true)
boolean isAlphanumericSpace String str
문자열이 모두 Character.isLetterOrDigit 이거나 공백이면 true
boolean isAlphaSpace String str
문자열이 모두 Character.isLetter 이거나 공백문자이면 true
boolean isBlank String str
문자열이 공백문자이거나 길이가 0이거나 null인경우 true
boolean isEmpty String str
문자열이 길이가 0이거나 null인경우 true
boolean isNotBlank String str
문자열이 공백문자도 아니고 길이가 0도 아니고 null도 아니면 true
boolean isNotEmpty String str
문자열이 길이가 0이 아니고 null도 아니면 true
boolean isNumeric String str
문자열이 모두 Character.isDigit 이면 true (모두 숫자이면 true)
boolean isNumericSpace String str
문자열이 모두 Character.isDigit 이거나 공백문자이면 true
boolean isWhitespace String str
문자열이 모두 Character.isWhitespace 이면 true (모두 공백이면 true)
String join Object array[]
array에서 문자열을 읽어와 모두 연결시킨다
String join Object array[], char separator
array에서 문자열을 읽어와 separator를 구분자로 연결시킨다
String join Object array[], String separator
array에서 문자열을 읽어와 separator를 구분자로 연결시킨다
String join Iterator iterator, char separator
iterator에서 문자열을 읽어와 separator를 구분자로 연결시킨다
String join Iterator iterator, String separator
iterator에서 문자열을 읽어와 separator를 구분자로 연결시킨다
int lastIndexOf String str, char searchChar
str에서 마지막 searchChar의 인덱스를 반환한다
int lastIndexOf String str, char searchChar, int startPos
str의 startPos 인덱스부터 마지막 searchChar의 인덱스를 반환한다
int lastIndexOf String str, String searchStr
str에서 마지막 searchStr의 인덱스를 반환한다
int lastIndexOf String str, String searchStr, int startPos
str의 startPos 인덱스부터 마지막 searchStr의 인덱스를 반환한다
int lastIndexOfAny String str, String searchStrs[]
str에서 searchStr의 문자열들중 포함하고 있는 문자열의 마지막 인덱스를 반환한다
String left String str, int len
str의 좌측에서 len 길이만큼 문자열을 구한다
String leftPad String str, int size
str의 길이가 size가 되도록 문자열 왼쪽에 ' '을 추가한다
String leftPad String str, int size, char padChar
str의 길이가 size가 되도록 문자열 왼쪽에 padChar를 추가한다
String leftPad String str, int size, String padStr
str의 길이가 size가 되도록 문자열 왼쪽에 padStr을 추가한다
String lowerCase String str
str을 소문자로 변환한다
String mid String str, int pos, int len
str의 pos 인덱스부터 len 길이만큼의 문자열을 구한다
String overlay String str, String overlay, int start, int end
str의 start부터 end까지overlay로 변환한다
String overlayString String text, String overlay, int start, int end
deprecated
String prechomp String str, String sep
deprecated
String repeat String str, int repeat
str을 repeat만큼 반복하여 반환한다
String replace String text, String repl, String width
text에서 모든 repl을 width로 변환한다
String replace String text, String repl, String width, int max
text에서 모든 repl을 width로 변환하는데 최대 max개만큼 변환한다
String replaceChars String str, char searchChar, char replaceChar
str에서 searchChar를 replaceChar로 모두 변환한다
String replaceChars String str, String searchChars, String replaceChars
str에서 searchChars를 replaceChars로 모두 변환한다
String replaceOne String text, String repl, String width
text에서 repl를 width로 변환하는데 첫번째 하나만 변환한다
String reverse String str
문자열을 앞뒤 순서를 바꾼다
String reverseDelimited String str, char separatorChar
separatorChar를 구분으로 문자열을 나눈 후 나눠진 단어들을 역순으로 바꾼다
String reverseDelimitedString String str, String separatorChars
deprecated
String right String str, int len
str의 len길이만큼 우측에서 문자열을 구한다
String rightPad String str, int size
str의 길이가 size가 되도록 문자열 오른쪽에 ' '을 추가한다
String rightPad String str, int size, char padChar
str의 길이가 size가 되도록 문자열 오른쪽에 padChar를 추가한다
String rightPad String str, int size, String padStr
str의 길이가 size가 되도록 문자열 오른쪽에 padStr을 추가한다
String[] split String str
공백문자를 구분자로 사용하여 분리한다
String[] split String str, char separatorChar
separatorChar를 구분자로 사용하여 분리한다
String[] split String str, String separatorChars
separatorChars를 구분자로 사용하여 분리한다
String[] split String str, String seperatorChars, int max
separatorChars를 구분자로 사용하여 분리한며 최대 max개 만큼한다. 배열의 크기는 max가 된다
String strip String str
문자열 좌우에 있는 공백문자를 제거한다 trim과 동일하다
String strip String str, String stripChars
문자열 좌우에 stripChars에 존재하는 문자를 제거한다
String[] stripAll String strs[]
배열에 있는 모든 문자열 좌우에 있는 공백문자를 제거한다
String[] stripAll String strs[], String stripChars
배열에 있는 모든 문자열 좌우에 stripChars에 존재하는 문자를 제거한다
String stripEnd String str, String stripChars
문자열 우측에만 strip 한다
String stripStart String str, String stripChars
문자열 좌측에만 strip 한다
String stripToEmpty String str
str이 null이면 ""를 반환하고 아니면 strip 한다
String stripToNull String str
str이 null이면 null을 반환하고 아니면 strip 한다
String substring String str, int start
str의 start 인덱스부터 문자열을 구한다
String substring String str, int start, int end
str의 start 부터 end 인덱스 까지 문자열을 구한다
String substringAfter String str, String separator
str의 처음 separator 부분부터 문자열을 구한다
String substringAfterLast String str, String separator
str의 마지막 separator부분부터 문자열을 구한다
String substringBefore String str, String separator
str의 처음 separator 부분까지 문자열을 구한다
String substringBeforeLast String str, String separator
str의 마지막 separator부분까지 문자열을 구한다
String substringBetween String str, String tag
str에서 tag 사이에 있는 문자열을 구한다
String substringBetween String str, String open, String close
str에서 open부터 close 까지 사이에 있는 문자열을 구한다
String swapCase String str
대문자는 소문자로 변환하고 소문자는 대문자로 변환한다
String trimToNull String str
str이 null이거나 length가 0이면 null을 반환하고 그렇지 않으면 좌우 공백문자를 제거한다
String trim String str
문자열 좌우 공백문자를 제거한다
String trimToEmpty String str
str이 null이면 ""를 반환하고 그렇지 않으면 좌우 공백문자를 제거한다
String uncapitalise String str
deprecated
String uncapitalize String str
문자열의 첫문자를 소문자로 변환한다
String upperCase String str
str을 대문자로 변환한다

사용법

모두 static 함수이기때문에 다음과 같이 사용하면 됩니다.

String text = StringUtils.replace(str, repl, width);


자료만들면서 정리한 엑셀 첨부 합니다 ^^


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

LOG4J



I. 들어가면서.. 그리고 log4j


log4j는 자바 어플리케이션에서 빠르고 효과적으로 로깅 할 수 있도록 도와주는 오픈 소스 프로젝트입니다.


로깅(logging)은 코드의 가독성을 떨어뜨리는 단점이 있지만 애플리케이션에 문제가 있을 때 개발자가 자세한 상황을 파악할 수 있도록 해 주며 테스팅시 빠질 수 없는 요소입니다.


아마도 여러분들은 여러 어플리케이션이 추가되면서 각 개발자들만의 독특한 로깅방식이 서로 썩이고 얽혀서 화면에 나타나는것을 많이 봤을겁니다 -_-;
즉 로깅방법을 통일할 필요가 있는것이죠. 모든 개발자가 특정 포맷에 맞추어서 로깅 한다면 한결 로깅하기도 편하겠지요


오픈 소스 프로젝트인 Log4j는 개발자들이 매우 손쉽고 다양한 형태로 로깅을 할 수 있도록 도와줍니다. 성능또한 우수해 더이상 System.out.println을 사용할 필요가 없습니다.



II. 다운로드


다운로드 http://logging.apache.org/log4j/docs/download.html

매뉴얼 http://logging.apache.org/log4j/docs/documentation.html

API spec http://logging.apache.org/log4j/docs/api/index.html


 

III. LOG4J 구조


일단 log4j를 잘 모르지만 그 구조만 살짝 살펴보고 넘어갑시다

log4j는 크게 3가지 요소로 구성되며 그 구조는 다음과 같습니다

① Logger(Category) : 로깅 메세지를 Appender에 전달합니다.

② Appender : 전달된 로깅 메세지를 파일에다 기록할 것인지, 콘솔에 출력할 것인지

                   아니면 DB에 저장할 것인지 매개체 역활을 합니다.

③ Layout : Appender가 어디에 출력할 것인지 결정했다면 어떤 형식으로 출력할 것이지

                출력 layout을 결졍합니다.

쉽죠?



IV. LOG4J 로깅 레벨


log4j는 다양한 로깅레벨을 지원합니다.


① FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.

② ERROR : 일반 에러가 일어 났을 때 사용합니다.

③ WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.

④ INFO : 일반 정보를 나타낼 때 사용합니다.

⑤ DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.


만약 로깅 레벨을 WARN 으로 설정하였다면 그 이상 레벨만 로깅하게 됩니다.

즉 WARN, ERROR, FATAL 의 로깅이 됩니다.



V. 샘플코드 1


jsp에서 사용하는 예제가 없어 만들어 봤습니다.


test.jsp


<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.Logger" %>

<%!
 static Logger logger = Logger.getLogger("test.jsp");
%>

<%
logger.fatal("fatal!!");

logger.fatal("fatal2!!", new NullPointerException("널입니다요"));

logger.error("error!", new NumberFormatException());

logger.error("error!2");

logger.warn("warn");

logger.info("info");

logger.debug("debug");
%>


결과 콘솔화면








static Logger logger = Logger.getLogger("test.jsp");

static 메소드 getLogger를 통해 logger 인스턴스를 가져옵니다.
getLogger에는 파라미터로 스트링 혹은 클래스를 사용하는데 jsp에서는 클래스를 파라미터로 주기에는 좀 애매합니다. 그냥 스트링으로 주도록 하지요


logger.fatal("fatal!!");
logger.fatal("fatal2!!", new NullPointerException("널입니다요"));
  
logger에 fatal 레벨의 메세지를 전달합니다. 다음 두가지 메소드를 지원하는군요

fatal(Object message)

fatal(Object message, Throwable t)

각 레벨마다 위처럼 두가지 메소드를 지원합니다.


지원 메쏘드
logger.fatal(Object message) logger.fatal(Object message, Throwable t)
logger.error(Object message) logger.error(Object message, Throwable t)
logger.warn(Object message) logger.warn(Object message, Throwable t)
logger.info(Object message) logger.info(Object message, Throwable t)
logger.debug(Object message) logger.debug(Object message, Throwable t)


VI. 샘플코드 2


서블릿의 경우 다음과 같이 코딩하면 되겠군요

TestServlet.java


import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class TestServlet extends HttpServlet {


    static Logger logger = Logger.getLogger(TestServlet.class);


    public void init(ServletConfig config) throws ServletException {
         super.init(config);
    }


    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         try {
              ...
   
              logger.info("Hellow World~");

              ...

          } catch (Exception e) {
              logger.error("Error at TestServlet", e);
          }
     }
}



VII. LOG4J 설정


log4j 설정은 프로그램 내에서 할 수 있지만 설정파일을 사용함으로서 좀더 유연하게 log4j환경을 만들 수 있습니다.


프로그램에서 설정

<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.*,java.io.* "
%>

<%!
 static Logger logger = Logger.getLogger("log4j.jsp");
%>

<%
String layout = "%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n";
String logfilename = "DailyLog.log";
String datePattern = ".yyyy-MM-dd ";

PatternLayout patternlayout = new PatternLayout(layout);
DailyRollingFileAppender appender = new DailyRollingFileAppender(patternlayout, logfilename, datePattern);
logger.addAppender(appender);
logger.setLevel(Level.INFO);
logger.fatal("fatal!!");
%>


property 파일에 설정
log4j.properties를 만들어 /WEB-INF/classes 밑에 놓으세요



log4j.rootLogger=INFO, stdout, rolling

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n

log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

log4j.appender.rolling.File=output.log

log4j.appender.rolling.Append=true

log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


#최상위 카테고리에 INFO로 레벨 설정 및 appender로 stdout, rolling을 정의

log4j.rootLogger=INFO, stdout, rolling

#stdout 어펜더는 콘솔에 뿌리겠다는 정의

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

#stdout 어펜더는 patternlayout을 사용하겠다는 정의

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

#페턴은 다음과 같이 포맷팅 하겠다는 것을 정의

log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


#역시나 rolling 어펜더는 파일로 처리한다라고 정의

log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

#로그 파일 이름은 output.log

log4j.appender.rolling.File=output.log

#true면 톰캣을 내렸다 올려도 파일이 리셋되지 않습니다.

log4j.appender.rolling.Append=true

#파일 최대 사이즈는 500KB로 설정

log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

#역시나 rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의

log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

#rolling 어펜더는 패턴 레이아웃 포맷

log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n



VIII. 설정 포맷


로그파일명 포맷 (DatePattern)
로그파일명 포맷입니다. 날짜, 시간 및 분단위로까지 로그 파일을 분리할 수 있습니다.

형식 설명
'.'yyyy-MM 매달 첫번째날에 로그파일을 변경합니다
'.'yyyy-ww 매주의 시작시 로그파일을 변경합니다.
'.'yyyy-MM-dd 매일 자정에 로그파일을 변경합니다.
'.'yyyy-MM-dd-a 자정과 정오에 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH 매 시간의 시작마다 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH-mm 매분마다 로그파일을 변경합니다.



PatternLayout 포맷
로그자체를 어떤 포맷으로 남길지 결정합니다.
layout에는 HTMLLayout, PatternLayout, SimpleLayout, XMLLayout등이 있으며 PatternLayout이 일반적으로 가장 많이 쓰입니다.


형식 설명
%p debug, info, warn, error, fatal 등의 priority 가 출력된다.
%m 로그내용이 출력됩니다
%d 로깅 이벤트가 발생한 시간을 기록합니다.
포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%% % 표시를 출력하기 위해 사용한다.
%n 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다.
%c 카테고리를 표시합니다
예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
%C 클래스명을 포시합니다.
예) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
%F 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l 로깅이 발생한 caller의 정보를 나타냅니다
%L 로깅이 발생한 caller의 라인수를 나타냅니다
%M 로깅이 발생한 method 이름을 나타냅니다.
%r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
%x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
%X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.

예시) (같은 색끼리 보시면 됩니다)

위의 test.jsp를 다음 포맷으로 출력해본다면

[%c] [%C] [%d] [%F] [%l] [%L] [%m] [%M] [%n] [%p] [%r] [%t] [%x] [%X]는 다음과 같다

[test.jsp] [org.apache.jsp.test_jsp] [2005-03-10 12:37:23,561] [test_jsp.java] [org.apache.jsp.test_jsp._jspService(test_jsp.java:64)] [64] [fatal!!] [_jspService] [개행] [FATAL] [765567] [http-8080-Processor25] [] []


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Commons Lang


 

I. Commons Lang란?


Commons-Lang은 java.lang에 있는 클래스처럼 기능적으로 필요한 유틸리티들을 모아놓은 클래스들의 집합입니다. 아마도 여러분들 역시 직접 유틸리티 클래스들을 만들어 사용하고 있을겁니다. 즉 코딩을 하다보면 이렇거 있었으면 좋겠다 싶은것들이 Commons Lang에 다 있다고 생각하시면 됩니다 ^^



II. 다운로드 및 설치


http://jakarta.apache.org/site/downloads/downloads_commons-lang.cgi 에서 다운받자!

설치는 각 어플리케이션의 클래스 패스에 복사합니다.



III. org.apache.commons.lang.SystemUtils


SystemUtils는 java.lang.System 클래스 처럼 시스템에 관련된 기능을 쉽게 이용 할 수 있도록 하는 시스템 관련 유틸리티입니다.

시스템 변수들을 가져올 때 매번 System.getProperty("JAVA_HOME")를 하기보다 SystemUtils.JAVA_HOME하면 간단하기 때문에 SystemUtils가 쓸만합니다.

SystemUtils의 변수들은 모두 static 변수이기 때문에 SystemUtils 객체생성 없이 SystemUtils.XXX로 바로 사용할 수 있습니다.


예) System.out.println("SystemUtils.FILE_ENCODING : "+SystemUtils.FILE_ENCODING);


반환값 변수명 설명
String SystemUtils.FILE_ENCODING System.getProperty("file.encoding")
String SystemUtils.FILE_SEPARATOR System.getProperty("file.separator")
String SystemUtils.JAVA_CLASS_PATH System.getProperty("java.class.path")
String SystemUtils.JAVA_CLASS_VERSION System.getProperty("java.class.version")
String SystemUtils.JAVA_COMPILER System.getProperty("java.compiler")
String SystemUtils.JAVA_EXT_DIRS System.getProperty("java.ext.dirs")
String SystemUtils.JAVA_HOME System.getProperty("java.home")
String SystemUtils.JAVA_IO_TMPDIR System.getProperty("java.io.tmpdir")
String SystemUtils.JAVA_LIBRARY_PATH System.getProperty("java.library.path")
String SystemUtils.JAVA_RUNTIME_NAME System.getProperty("java.runtime.name")
String SystemUtils.JAVA_RUNTIME_VERSION System.getProperty("java.runtime.version")
String SystemUtils.JAVA_SPECIFICATION_NAME System.getProperty("java.specification.name")
String SystemUtils.JAVA_SPECIFICATION_VENDOR System.getProperty("java.specification.vendor")
String SystemUtils.JAVA_SPECIFICATION_VERSION System.getProperty("java.specification.version")
String SystemUtils.JAVA_VENDOR System.getProperty("java.vendor")
String SystemUtils.JAVA_VENDOR_URL System.getProperty("java.vendor.url")
String SystemUtils.JAVA_VERSION System.getProperty("java.version")
String SystemUtils.JAVA_VM_INFO System.getProperty("java.vm.info")
String SystemUtils.JAVA_VM_NAME System.getProperty("java.vm.name")
String SystemUtils.JAVA_VM_SPECIFICATION_NAME System.getProperty("java.vm.specification.name")
String SystemUtils.JAVA_VM_SPECIFICATION_VENDOR System.getProperty("java.vm.specification.vendor")
String SystemUtils.JAVA_VM_SPECIFICATION_VERSION System.getProperty("java.vm.specification.version")
String SystemUtils.JAVA_VM_VENDOR System.getProperty("java.vm.vendor")
String SystemUtils.JAVA_VM_VERSION System.getProperty("java.vm.version")
String SystemUtils.LINE_SEPARATOR System.getProperty("line.separator")
String SystemUtils.OS_ARCH System.getProperty("os.arch")
String SystemUtils.OS_NAME System.getProperty("os.name")
String SystemUtils.OS_VERSION System.getProperty("os.version")
String SystemUtils.PATH_SEPARATOR System.getProperty("path.separator")
String SystemUtils.USER_COUNTRY System.getProperty("user.country") != null ? System.getProperty("user.country") : System.getProperty("user.region")
String SystemUtils.USER_DIR System.getProperty("user.dir")
String SystemUtils.USER_HOME System.getProperty("user.home")
String SystemUtils.USER_LANGUAGE System.getProperty("user.language")
String SystemUtils.USER_NAME System.getProperty("user.name")
float JAVA_VERSION_FLOAT 자바 버젼을 float형으로
int JAVA_VERSION_INT 자바 버젼을 int형으로
boolean IS_JAVA_1_1 자바버젼이 1.1인가?
boolean IS_JAVA_1_2 자바버젼이 1.2인가?
boolean IS_JAVA_1_3 자바버젼이 1.3인가?
boolean IS_JAVA_1_4 자바버젼이 1.4인가?
boolean IS_JAVA_1_5 자바버젼이 1.5인가?
boolean IS_OS_AIX 운영체제가 AIX인가?
boolean IS_OS_HP_UX 운영체제가 HP Unix인가?
boolean IS_OS_IRIX 운영체제가 IRIX인가?
boolean IS_OS_LINUX 운영체제가 LINUX인가?
boolean IS_OS_MAC 운영체제가 MAC인가?
boolean IS_OS_MAC_OSX 운영체제가 MAC OSX인가?
boolean IS_OS_OS2 운영체제가 OS2인가?
boolean IS_OS_SOLARIS 운영체제가 SOLARIS인가?
boolean IS_OS_SUN_OS 운영체제가 SUN인가?
boolean IS_OS_WINDOWS 운영체제가 WINDOW인가?
boolean IS_OS_WINDOWS_2000 운영체제가 WINDOW 2k인가?
boolean IS_OS_WINDOWS_95 운영체제가 WINDOW 95인가?
boolean IS_OS_WINDOWS_98 운영체제가 WINDOW 98인가?
boolean IS_OS_WINDOWS_ME 운영체제가 WINDOW ME인가?
boolean IS_OS_WINDOWS_NT 운영체제가 WINDOW NT인가?
boolean IS_OS_WINDOWS_XP 운영체제가 WINDOW XP인가?

이를 바탕으로 제 컴(win2k)에서 출력한 결과입니다.


변수명 출력결과
SystemUtils.FILE_ENCODING MS949
SystemUtils.FILE_SEPARATOR \
SystemUtils.JAVA_CLASS_PATH C:\Tomcat 4.1\bin\bootstrap.jar
SystemUtils.JAVA_CLASS_VERSION 48.0
SystemUtils.JAVA_COMPILER null
SystemUtils.JAVA_EXT_DIRS C:\j2sdk1.4.2_06\jre\lib\ext
SystemUtils.JAVA_HOME C:\j2sdk1.4.2_06\jre
SystemUtils.JAVA_IO_TMPDIR C:\DOCUME~1\kitty1\LOCALS~1\Temp\
SystemUtils.JAVA_LIBRARY_PATH C:\j2sdk1.4.2_06\bin;.;C:\WINNT\system32;C:\WINNT;
SystemUtils.JAVA_RUNTIME_NAME Java(TM) 2 Runtime Environment, Standard Edition
SystemUtils.JAVA_RUNTIME_VERSION 1.4.2_06-b03
SystemUtils.JAVA_SPECIFICATION_NAME Java Platform API Specification
SystemUtils.JAVA_SPECIFICATION_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_SPECIFICATION_VERSION 1.4
SystemUtils.JAVA_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_VENDOR_URL http://java.sun.com/
SystemUtils.JAVA_VERSION 1.4.2_06
SystemUtils.JAVA_VM_INFO mixed mode
SystemUtils.JAVA_VM_NAME Java HotSpot(TM) Client VM
SystemUtils.JAVA_VM_SPECIFICATION_NAME Java Virtual Machine Specification
SystemUtils.JAVA_VM_SPECIFICATION_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_VM_SPECIFICATION_VERSION 1.0
SystemUtils.JAVA_VM_VENDOR Sun Microsystems Inc.
SystemUtils.JAVA_VM_VERSION 1.4.2_06-b03
SystemUtils.LINE_SEPARATOR  
SystemUtils.OS_ARCH x86
SystemUtils.OS_NAME Windows 2000
SystemUtils.OS_VERSION 5.0
SystemUtils.PATH_SEPARATOR ;
SystemUtils.USER_COUNTRY KR
SystemUtils.USER_DIR C:\Tomcat 4.1
SystemUtils.USER_HOME C:\Documents and Settings\kitty1
SystemUtils.USER_LANGUAGE ko
SystemUtils.USER_NAME kitty1
JAVA_VERSION_FLOAT 1.42
JAVA_VERSION_INT 142
IS_JAVA_1_1 false
IS_JAVA_1_2 false
IS_JAVA_1_3 false
IS_JAVA_1_4 true
IS_JAVA_1_5 false
IS_OS_AIX false
IS_OS_HP_UX false
IS_OS_IRIX false
IS_OS_LINUX false
IS_OS_MAC false
IS_OS_MAC_OSX false
IS_OS_OS2 false
IS_OS_SOLARIS false
IS_OS_SUN_OS false
IS_OS_WINDOWS true
IS_OS_WINDOWS_2000 true
IS_OS_WINDOWS_95 false
IS_OS_WINDOWS_98 false
IS_OS_WINDOWS_ME false
IS_OS_WINDOWS_NT false
IS_OS_WINDOWS_XP false

위의 결과값을 보니 너무 유용하게 사용되겠죠? ^^


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Jakarta POI



VII. 엑셀 쓰기예제


쓰기도 역시 읽기와 비슷합니다.

엑셀 워크북을 생성합니다. 행과 셀을 생성하려면 당연한 절차겠죠?

HSSFWorkbook workbook = new HSSFWorkbook();


시트를 생성합니다.

시트명을 파라미터로 바로 생성 합니다.

HSSFSheet sheet = workbook.createSheet("sheet name");


만약 한글로 시트명을 만들려면 다음과 같이 인코딩이 필요합니다.

HSSFSheet sheet = workbook.createSheet();

workbook.setSheetName( 0 , "한글" , HSSFWorkbook.ENCODING_UTF_16 );


셀에 사용할 스타일을 미리 생성해 둡니다.

HSSFCellStyle style = wb.createCellStyle();
style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style.setBottomBorderColor(HSSFColor.BLACK.index);
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style.setLeftBorderColor(HSSFColor.GREEN.index);
style.setBorderRight(HSSFCellStyle.BORDER_THIN);
style.setRightBorderColor(HSSFColor.BLUE.index);
style.setBorderTop(HSSFCellStyle.BORDER_MEDIUM_DASHED);
style.setTopBorderColor(HSSFColor.BLACK.index);

등 여러가지 스타일을 만들 수 있습니다.


스타일은 다음 주소를 참고하세요

http://jakarta.apache.org/poi/apidocs/org/apache/poi/hssf/usermodel/HSSFCellStyle.html


로우를 하나 생성합니다.

HSSFRow row = sheet.createRow(0);


셀츨 하나 생성하여 스타일을 주고 값을 입력합니다.

HSSFCell cell = row.createCell((short)0);

cell.setCellStyle(style);

cell.setCellValue("jakarta project!");


만약 한글을 입력한다면 인코딩 해야 하며 값 세팅전에 해야 합니다.

cell.setEncoding(HSSFCell.ENCODING_UTF_16);  //한글 처리

cell.setCellStyle(style);

cell.setCellValue("자카드타 프로젝트!");


모든 셀이 다 입력되었으면 파일을 만듭니다.

FileOutputStream fs = new FileOutputStream("excelfile.xls");
workbook.write(fs);
fs.close();



VIII. 쓰기샘플 소스


<%@ page language="java" contentType="text/html;charset=euc-kr" %>
<%@ page import="java.io.*" %>
<%@ page import="org.apache.poi.poifs.dev.*" %>
<%@ page import="org.apache.poi.hssf.record.*" %>
<%@ page import="org.apache.poi.hssf.record.formula.*" %>
<%@ page import="org.apache.poi.hssf.model.*" %>
<%@ page import="org.apache.poi.hssf.usermodel.*" %>
<%@ page import="org.apache.poi.hssf.util.*" %>


<html>
<body>

<%

    String filepath = "C:\\Tomcat 5.0\\webapps\\ROOT\\write.xls";


    try {

        String[] cell_value = {"자카르타","프로젝트","www.jakartaproject.com"};


        HSSFWorkbook workbook = new HSSFWorkbook();


        HSSFSheet sheet = workbook.createSheet();
        workbook.setSheetName(0 , "한글명" ,HSSFWorkbook.ENCODING_UTF_16);


        HSSFCellStyle style = workbook.createCellStyle();
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBottomBorderColor(HSSFColor.BLACK.index);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setLeftBorderColor(HSSFColor.GREEN.index);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setRightBorderColor(HSSFColor.BLUE.index);
        style.setBorderTop(HSSFCellStyle.BORDER_MEDIUM_DASHED);
        style.setTopBorderColor(HSSFColor.BLACK.index);           


        HSSFRow row = sheet.createRow(0);
        for (int i = 0 ; i < cell_value.length; i++){
            HSSFCell cell = row.createCell((short)i);
            cell.setEncoding(HSSFCell.ENCODING_UTF_16);
            cell.setCellStyle(style);
            cell.setCellValue(cell_value[i]);
        }
           
        FileOutputStream fs = null;
        try {
            fs = new FileOutputStream(filepath);
            workbook.write(fs);
        } catch (Exception e) {
        } finally {
            if (fs != null) fs.close();
        }
       
    } catch (Exception e) {
%>
        Error occurred:  <%= e.getMessage() %>
<%  
        e.printStackTrace();
    }   
   
%>

</body>
</html>



자 결과화면 입니다.





성공!


위의 소스를 기본으로 한다면 그리 어렵지 않을겁니다 ^^


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Jakarta POI


 

I. POI 란?


일반적으로 POI가 엑셀파일을 쓰는 컴퍼넌트로 알려져 있으나 POI는 프로젝트 이름입니다.
즉 POI는 Microsoft Format File을 액세스 할 수 있는 API를 제공합니다. (한마디로 자바에서 MS파일을 읽고 쓸수있도록 지원합니다.)


POI안에는 여러 컴퍼넌트들이 있습니다.

POIFS
Microsoft의 OLE2 포맷 형식의 문서를 자바로 읽고 쓸수 있는 컴퍼넌트입니다
기본적으로 POI의 모든 컴퍼넌트들이 POIFS를 사용합니다.
HSSF
Microsoft의 엑셀파일을 읽고 쓸수 있도록 지원하는 컴퍼넌트입니다.
HWPF
Microsoft의 워드파일을 읽고 쓸수 있도록 지원하는 컴퍼넌트입니다.
이 컴퍼넌트는 디자인 초기단계입니다.
HPSF
Microsoft의 OLE2 포맷 형식의 문서 속성을 어플리케이션에서 사용 할수 있도록 지원하는 컴퍼넌트입니다.
현재 읽기 기능만 제공합니다


워드파일을 핸들링 하는 HWPF는 초기단계라 사용을 못하지만 기대는 되는군요 ^^


ps. 영어사전을 찾아보니 poi는 하와이의 토란 요리를 뜻하더군요.

우리나라말로 하니 자카르타 토란 프로젝트 쯤 될라나? ㅎㅎ



II. 다운로드 및 설치


다운로드 받으러 갑시다~!

http://jakarta.apache.org/site/downloads/downloads_poi.cgi

현재 2.5.1버젼입니다.

다운받은 파일을 압축을 풀면 *.jar 파일들이 있을겁니다 이 파일들을 자신의 어플리케이션 /lib/에 복사합시다


POI API http://jakarta.apache.org/poi/apidocs/index.html

Quick Guide http://jakarta.apache.org/poi/hssf/quick-guide.html



III. Formula(수식) 지원에 관해..


엑셀을 읽고 쓸때 수식을 지원합니다.
org.apache.poi.hssf.usermodel.HSSFCell의 setCellFormula("formulaString") 메쏘드는 스프레드시트에 수식을 추가하는데 사용되며 getCellFormula() 메쏘드는 수식을 대표하는 문자열을 해석하는데 사용됩니다. 하지만 엑셀에서 사용하는 수식을 모두 사용 할 수는 없습니다.


지원되는 부분
-. 셀 참조, 시트참조, 지역참조
-. 상대적 혹은 절대적 참조
-. 수연산 및 논리연산
-. 시트 혹은 매크로 함수

-. 수식 결과값 반환


부분적 지원
문자열을 포함하는 수식을 해석할 수는 있지만 문자열값을 반환하는 수식은 아직 지원하지 않습니다.

지원되지 않는 부분

-. 배열 수식
-. 1진법 수식
-. 3D 참조
-. 에러 값 (cells containing #REF's or #VALUE's)


 

IV. 기본객체


가장 기본이되는 객체가 다음 4가지 입니다

이름에서 무엇을 뜻하는지 대강 짐작 할 수 있겠죵?


HSSFWorkbook - 엑셀 워크북을 말합니다.
HSSFSheet - 엑셀 쉬트를 나타냅니다.
HSSFRow - 엑셀에서 특정 행입니다.
HSSFCell - 엑셀에서 특정 행에대한 특정 셀입니다


위 4가지 객체는 앞으로 계속 나올겁니다. 눈여겨 미리 봐 둡시다. @.@



V. 엑셀 읽기 예제


POSFS을 이용하여 엑셀 워크북을 생성합니다.


POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream("excelfile.xls"));
HSSFWorkbook workbook = new HSSFWorkbook(fs);



생성된 워크북을 이용하여 시트 수만큼 돌면서 엑셀 시트 하나씩을 생성합니다.


int sheetNum = workbook.getNumberOfSheets();

for (int k = 0; k < sheetNum; k++) {
   System.out.println("Sheet Number : "+k);

   System.out.println(Sheet Name : " + workbook.getSheetName(k));
   HSSFSheet sheet = workbook.getSheetAt(k);

}



생성된 시트를 이용하여 그 행의 수만큼 돌면서 행을 하나씩 생성합니다.


int rows = sheet.getPhysicalNumberOfRows();

for (int r = 0; r < rows; r++) {
   HSSFRow row   = sheet.getRow(r);

   System.out.println("Row : "+row.getRowNum());

}



역시나 생성된 행을 이용하여 그 셀의 수만큼 돌면서 셀을 하나씩 생성합니다.


int cells = row.getPhysicalNumberOfCells();

for (short c = 0; c < cells; c++) {              <--!! short 형입니다. 255개가 max!
    HSSFCell cell  = row.getCell(c);

    int celltype = cell.getCellType();

    ...

}

셀을 생성하여 셀 타입에 따라 처리를 해주면 끝~


주의사항

만약 엑셀에서 A열에 아무런 값이 없으면 그 행은 읽지 못합니다.

행을 읽지 못하니 셀또한 처리 할 수 없습니다



VI. 엑셀읽기 샘플소스


샘플 데이터








A열은 B열에 대한 셀 타입을 나타내며 C열은 D열에대한 셀 타입을 나타냅니다.

즉 B:1 의 123456의 셀 타입은 A:1 일반 이라는 것이며 마찬가지로

D:1의 2005-02-09의 셀타입은 C:1 사용자정의로 세팅하였다는 겁니다


이 엑셀의 데이터를 다음 소스로 읽어 보겠습니다.


<%@ page
language="java"
contentType="text/html;charset=euc-kr"
import="java.io.*,
 org.apache.poi.poifs.filesystem.POIFSFileSystem,
 org.apache.poi.hssf.record.*,
 org.apache.poi.hssf.model.*,
 org.apache.poi.hssf.usermodel.*,
 org.apache.poi.hssf.util.*" %>


<html>
<head><title>Read example</title></head>
<body>

<%

  String excelfile = "C:\\Tomcat 5.0\\webapps\\ROOT\\example.xls";

  try {
       POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(excelfile));


       //워크북을 생성!               

       HSSFWorkbook workbook = new HSSFWorkbook(fs);

       int sheetNum = workbook.getNumberOfSheets();


       for (int k = 0; k < sheetNum; k++) {


            //시트 이름과 시트번호를 추출
%>

            <br><br>
            Sheet Number <%= k %> <br>
            Sheet Name <%= workbook.getSheetName(k) %><br>
<%
            HSSFSheet sheet = workbook.getSheetAt(k);
            int rows = sheet.getPhysicalNumberOfRows();


            for (int r = 0; r < rows; r++) {


                // 시트에 대한 행을 하나씩 추출
                HSSFRow row   = sheet.getRow(r);
                if (row != null) {
                     int cells = row.getPhysicalNumberOfCells();
%>
                     ROW  <%= row.getRowNum() %> <%=cells%></b><br>
<%

                     for (short c = 0; c < cells; c++) {


                         // 행에대한 셀을 하나씩 추출하여 셀 타입에 따라 처리
                         HSSFCell cell  = row.getCell(c);
                         if (cell != null) {
                              String value = null;

                              switch (cell.getCellType()) {

                                   case HSSFCell.CELL_TYPE_FORMULA :
                                       value = "FORMULA value=" + cell.getCellFormula();
                                        break;
                                   case HSSFCell.CELL_TYPE_NUMERIC :
                                       value = "NUMERIC value=" + cell.getNumericCellValue(); //double
                                       break;
                                  case HSSFCell.CELL_TYPE_STRING :
                                       value = "STRING value=" + cell.getStringCellValue(); //String
                                       break;
                                  case HSSFCell.CELL_TYPE_BLANK :
                                      value = null;
                                     break;
                                 case HSSFCell.CELL_TYPE_BOOLEAN :
                                     value = "BOOLEAN value=" + cell.getBooleanCellValue(); //boolean
                                    break;
                                case HSSFCell.CELL_TYPE_ERROR :
                                     value = "ERROR value=" + cell.getErrorCellValue(); // byte
                                     break;
                                default :
                             }
%>        
                          <%= "CELL col=" + cell.getCellNum() + " VALUE=" + value %> <br>
<%
                        }
                    }
                }
            }
       }
   } catch (Exception e) {
%>
       Error occurred:  <%= e.getMessage() %>
<%  
       e.printStackTrace();
    }

%>


</body>
</html>


위 소스의 결과입니다.


Sheet Number 0
Sheet Name 한글
ROW 0 4
CELL col=0 VALUE=STRING value=일반
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=사용자정의
CELL col=3 VALUE=NUMERIC value=38392.0
ROW 1 4
CELL col=0 VALUE=STRING value=숫자
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=날짜 (yy-m-d h:mm)
CELL col=3 VALUE=NUMERIC value=38393.0
ROW 2 4
CELL col=0 VALUE=STRING value=통화
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=날짜 (yy年 mm月 dd日)
CELL col=3 VALUE=NUMERIC value=38394.0
ROW 3 4
CELL col=0 VALUE=STRING value=텍스트
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=날짜 (yyyy년 mm월 dd일)
CELL col=3 VALUE=NUMERIC value=38395.0


 

결과를 보니 사용자가 지정한 셀 타입에 관계없이

숫자관련 셀은 POI에서 모두 숫자 타입으로 인식해 버렸습니다.

날짜 역시 지정한 셀 타입에 관계없이 모두 숫자 타입으로 인식해 버리는군요!

그럼 어떻게 날짜를 제대로 표현할까요?

날짜 타입을 제대로 나타내기 위해서는 날짜 Cell에는 getDateCellValue()를 사용하면

정상적으로 처리 할 수 있습니다.

SimpleDateformat sdf = new SimpleDateformat("yyyy-MM-dd hh:mm");
String date = sdf.format(cell.getDateCellValue());

등을 이용하면 나타내고자 하는 알짜를 표현 하기 더 쉽겠지요

나머지 수식을 가져 올때도 마찬가지입니다. 이런 사항을 도표로 나타내보았습니다.


org.apache.poi.hssf.usermodel.HSSFCell 에는 모두 6가지의 Cell Type이 있는데,

cell.getCellType()을 하면 그 셀의 반환값을 알 수 있으며 그에 상응하는 static 필드타입은 다음과 같습니다.


셀타입 필드타입

함수

함수반환값
0 CELL_TYPE_NUMERIC

getNumericCellValue()

-> 숫자 타입일때

getDateCellValue()

-> 날짜 타입일때

double


Date


1 CELL_TYPE_STRING

getStringCellValue()

String
2 CELL_TYPE_FORMULA

getCellFormula()

-> 수식자체를 가져올때

getNumericCellValue()

-> 수식 반환값이 숫자일때

getStringCellValue()

-> 수식 반환값이 문자일때

String


double


String

3 CELL_TYPE_BLANK

4 CELL_TYPE_BOOLEAN

getBooleanCellValue()

boolean
5 CELL_TYPE_ERROR

getErrorCellvalue()

byte

이번시간에는 POI 프로젝트를 이용하여 엑셀 파일을 읽어보았습니다.

다음 시간에는 엑셀파일에 쓰는 핸드링을 해 보도록 하지요~


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Commons-Fileupload

 


I. Commons-fileupload 란?

 

커먼스의 파일 업로드 패키지는 사용법이 쉬우며 쓸만한 함수들을 많이 지원합니다.

SmartUpload, MultipartRequest, Commons fileupload등을 모두 사용해 보았지만 개인적으로 가장 애착이 가는 파일 업로드입니다.

아쉬운점은 Commons-fileupload는 지난 2003년 6월 1.0버젼으로 정식 릴리즈 되었지만 그 이후로 이렇다 할 패치나 보안이 전혀 안되었다는 것입니다. 이말은 파일 업로드 자체가 그리 복잡한 패키지가 아니며 첫 정식 버젼이 그만큼 완벽하다는 것을 반증하는 말이기도 하겠지요 ^^



II. 다운로드 및 설치

 

자 다운로드 받으러 갑시다.

http://jakarta.apache.org/site/downloads/downloads_commons-fileupload.cgi

설치는 역시나 /WEB-INF/lib/ 폴더에 복사합니다.



III. 파일 업로드 처리 단계

 

먼저 파일 업로드된 아이쳄을 처리하기 전에 유효한 Request인지 확인해야 합니다.

boolean isMultipart = FileUpload.isMultipartContent(request);

이 코드는 현재 request가 multipart/form-data로 데이터를 전송했는지 유무를 true/false로 반환합니다.


업로드된 아이템이 매우 작다면 메모리에서 처리합니다.


큰 아이템이라면 임시 파일을 만들어 디스크에 저장하여 처리합니다.


너무 큰 아이템이라면 당연히 거부해야 합니다.


그렇지 않고 디폴트로 설정한 메모리, 최대값의 범위를 초과하지 않는다면 업로드를 시작합니다.

② ~ ⑤ 까지를 다음과 같이 간단히 할 수 있습니다.

// 파일 업로드 핸들러를 생성
DiskFileUpload upload = new DiskFileUpload();

// 한번에 메모리에 저장할 사이즈 설정

upload.setSizeThreshold(yourMaxMemorySize);

// 파일 업로드 최대 사이즈를 설정

upload.setSizeMax(yourMaxRequestSize);

// 파일 업로드 경로를 설정
upload.setRepositoryPath(yourTempDirectory);

이제 전송된 request를 parsing하여 아이템들을 추출합니다.

List /* FileItem */ items = upload.parseRequest(request);

위에서 설정한 값들을 request parsing단위로 따로 설정 할 수 있습니다.

List /* FileItem */ items = upload.parseRequest(request,
        yourMaxMemorySize, yourMaxRequestSize, yourTempDirectory);


이렇게 추출된 items를 이제 폼필드 타입에 따라 적절히 처리 합니다.

Iterator iter = items.iterator();
while (iter.hasNext()) {
    FileItem item = (FileItem) iter.next();

    // 파일 타입이 아닌 다른 폼필드라면
    if (item.isFormField()) {
        processFormField(item);


    // 파일 타입 폼필드라면
    } else {
        processUploadedFile(item);
    }
}


폼필드에 따른 처리

만약 폼필드가 파일 이외의 파라미터라면 다음과 같이 처리하면 됩니다.

if (item.isFormField()) {
   //파라미터 이름

    String name = item.getFieldName();          

   //파라미터 값

    String value = item.getString();              
    ...
}


만약 폼필드가 파일 파라미터라면 다음과 같이 처리하면 됩니다.

if (!item.isFormField()) {

    // 파라미터 이름
    String fieldName = item.getFieldName();

    // 파일 이름 (경로포함)
    String fileName = item.getName();
    String contentType = item.getContentType();

    // 업로드한 파일이 메모리에 저장된 상태면 true, 거렇지 않고 임시 파일로 저장된 경우 false를 리턴
    boolean isInMemory = item.isInMemory();

    // 파일사이즈
    long sizeInBytes = item.getSize();
    ...


   // 파일 객체를 하나 만들어 업로드 완료!

   File uploadedFile = new File(...);

   item.write(uploadedFile);
}


파일 업로드 방식

파일 업로드 방식에는 모두 3가지가 존재합니다.

-. FileItem.write(File file);

-. FileItem.getInputStream();

-. FileItem.get()


write 는 직접적으로 업로드한 파일을 저장하는 가장 일반적이고 간단한 방식입니다.

나머지 두 방식은 스트림을 사용하여 업로드한 파일이 어떤 특별한 처리를 할 때 사용합니다.  혹은 데이터베이스에 바로 저장 할 경우에도 사용될 수 있습니다. 특히 get()은 메모리에 모두 할당하여 작업을 하니 주의를 요합니다.



VI. 샘플코드


import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;


public class UploadServlet extends HttpServlet {


   String upload = null;


   public void init(ServletConfig config) throws ServletException {
      super.init(config);  
      upload = config.getServletContext().getRealPath("/upload/")
   }
 
   public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

      try {
 
         if (FileUpload.isMultipartContent(request)) {

            DiskFileUpload fileUload = new DiskFileUpload();
            fileUpload.setRepositoryPath(upload);
            fileUpload.setSizeMax(100*1024*1024);
            fileUpload.setSizeThreshold(1024*50);
   
            List items = fileUpload.parseRequest(request);


            Iterator iterator = items.iterator();
            while (iterator.hasNext()) {


                FileItem item = (FileItem) iterator.next();

                if (!item.isFormField()) {
                    if (fileItem.getSize() > 0) {
                        //파일 이름을 가져온다      
                        String filename = fileItem.getName().substring(fileItem.getName().lastIndexOf("\\")+1);
       
                         try {
                              File file = new File(upload+filename);
                              fileItem.write(file);
                              } catch (IOException e) {
                                   System.out.println(e);
                              }      
                         }
                    }
               }
          }

      } catch (org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException e) {

          // 파일 사이즈 초과시 발생하는 익셉션
          System.out.println("파일 사이즈가 100메가 보다 더 초과되었습니다");
      } catch (Exception e) {
          System.out.println("업로드시 예기치 못한 오류가 발생하였습니다");
      }
   }
}


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

Posted by 1010
반응형

Apache JMeter



VI. 테스트 구성단위

 

앞의 예제에서 대강의 테스팅 흐름을 살펴보았습니다. 이제 각각의 요소별로 자세히 알아봅시다.


크게 보면 다음 7가지 요소들로 나누어집니다.

1. 쓰레드 그룹 (Thread Group)

쓰레드 그룹은 테스팅에 있어 시작점이 되며 모든 요소들은 최소한 하나의 쓰레드 그룹에 속해야 합니다.


쓰레드 프러퍼티부분이 가장 중요하며 테스팅의 효과를 어떻게 얻을 것인지 결정하게 됩니다. 쓰레드 프러퍼티는 다음과 같이 생각할 수 있습니다.

① 쓰레드 갯수 : 몇명으로 테스트할 것인가?

② 램업 주기 : 얼마나 빨리 테스트 할 것인가?

③ 반복 수 : 얼마나 많이 테스트 할 것인가?


만약 쓰래드 갯수를 20개, 랩업 주기를 100초, 반복 수를 2로 하였다면 전체 테스트 수는 40(20x2 )번이 테스팅 되며, 한 쓰레드가 실행 후 다음 쓰레드 실행 까지는 5(100/20)초가 걸리며 총 수행 시간은 100초 입니다.

쉽죵?

만약 무한반복을 하고자 한다면 forever에 체크하세요


1.9버젼부터는 스케쥴 기능이 추가되었습니다. 시작시간과 종료시간을 입력하면 메뉴에서 RUN하지 않아도 시작시간에 맞춰 실행하게 됩니다.


2. 컨트롤러 (Controller)

컨트롤러란 서버에 테스트 요청을 전달 할 때 무엇을 언제 보낼지를 지시합니다.

샘플러(Sampler, 무엇을)와 로직(Logic Controller, 언제) 두가지 타입이 있습니다.


2-1. 샘플러(Sampler)

샘플러는 테스트 서버에게 HTTP 요청을 보내고 싶으면 HTTP 요청 샘플러를, FTP 요청을 보내고 싶으면 FTP 요청 샘플러를 추가하면 됩니다.


좌측의 그림은 샘플러 종류를 나타냅니다.

정말 다양한 요청이 지원되고 있습니다.


만약 서버에 같은 타입의 요청을 다중으로 보내고자 한다면 Config Element의 디폴트 요소를 사용하는것이 편리합니다. (예: HTTP Request Default)










2-2. 로직 컨트롤러(Logic Controller)

로직 컨트롤러는 언제 요청을 전달해야 하는지 결정합니다. 다음 예를봅시다.

  • Test Plan
    • Thread Group
      • Once Only Controller
        • Login Request (an HTTP Request)
      • Load Search Page (HTTP Sampler)
      • Interleave Controller
        • Search "A" (HTTP Sampler)
        • Search "B" (HTTP Sampler)
        • HTTP default request (Configuration Element)
      • HTTP default request (Configuration Element)
      • Cookie Manager (Configuration Element)

    HTTP default request와 Cookie Manager가 Thread Group에 추가되어 있다는 것을 유의깊게 봅시다. 그리고 Interleave Controller에도 HTTP default request가 있습니다.

    ① Once Only 컨크롤러에 의해 로그인 요청은 딱 한번 요청이 됩니다. 이후의 반복작업에서는 이 작업이 스킵이 되게 됩니다.

    ② 그다음 검색 페이지를 요청합니다. 이 요청은 어떠한 컨트롤러도 개입이 되어있지 않습니다.

    ③ 검색페이지에서 "A"를 검색합니다. Interleave 컨트롤러의해 다음 반복 작업때에는 "B"를 검색합니다. 즉 순서를 돌아가며 실행 된다는 것입니다.


    이처럼 로직 컨트롤러는 다양한 구성요소의 조합을 가능 하게 해 줍니다.


    좌측 그림은 컨트롤러의 종류를 보여줍니다.


    대부분 이름만으로 짐작 가능한 컨트롤러이니 그리 어렵지는 않을 겁니다 ^^
















    3. 리스너(Listener)

    리스너는 테스팅한 결과의 정보를 나타내는데 사용됩니다. 즉 결과를 레포팅 해주는 것이죠

    어떤 위치든지 추가될 수 있으며 그 요소에 해당하는 데이터나 그 이하 노드에 해당하는 데이터들만 수집하게 됩니다.

    가장 간단한 Graph Result 리스너는 응답시간을 그래프에 뿌려줍니다.


    좌측 그림은 리스너의 종류들입니다.













    4. 타이머(Timer)

    기본적으로 JMeter는 요청들을 쉴틈없이 테스트 서버로 보내게 되는데 만약 아주 짧은 시간에 상당한 양의 요청들을 보낸다면 서버가 뻗어 버릴지도 모릅니다.

    타이머는 쓰레드가 만들어 내는 각 요청들 사이에 특정 시간만큼 딜레이를 주는 역할을 합니다.








    5. 어서션 (Assertions)

    어서션은 테스트 요청에 대한 응답이 제대로 된 응답인지 그 유효성을 검증해줍니다.

    예를들어 테스트 요청에 대한 응답이 어느 특정 텍스트를 포함하고 있는지를 확인 해 볼 수 있다는 것입니다.

    어서션의 결과를 볼려면 어서션 리스너를 추가해야 한답니다.


    좌측 그림은 어서션의 종류를 나타냅니다.









    이상 JMeter의 구성요소들에 대해 간략히 살펴보았습니다.


    다음 레퍼런스 사이트를 참조 하면서 테스트 계획을 구성, 실행한다면 상당히 괜찮은 테스트 시나리오를 만들 수 있습니다.

    http://jakarta.apache.org/jmeter/usermanual/component_reference.html


    =============================================

    본문서는 자유롭게 배포/복사 할수 있지만

    이문서의 저자에 대한 언급을 삭제하시면 안됩니다

    저자 : GoodBug (unicorn@jakartaproject.com)

    최초 : http://www.jakartaproject.com 

    =============================================

  • Posted by 1010
    반응형

    Apache JMeter



    I. Apache JMeter

     

    Apache JMeter는 100% 순수 자바로 만든 퍼포먼스 테스팅 도구 입니다.

    기본적으로는 Web Application을 테스팅 하는데 디자인 되었지만 다른 기능들도 테스트 할 수 있도록 확장되었답니다


    그럼 Apache JMeter로 무엇을 할 수 있을까요?

    JMeter는 정적인 것 뿐만 아니라 동적인 자원 (files, Servlet, Perl, Java Object, DataBase 와 Queries, FTP Servers 등) 둘다 성능을 테스트 하는데 사용됩니다. 즉 서버나 네트웍 혹은 Object에 스트레스를 가하여 다양한 형태의 상태에서 성능을 전반적으로 분석 할 수 있도록 도와준다는 겁니다.


    그럼 JMeter의 특징을 간단히 살펴보죵.

    -. HTTP나 FTP서버 뿐만 아니라 임의의 데이터베이스 쿼리도 성능을 테스트 할 수 있습니다.

    -. 100% 순수 자바로 구현되었기 때문에 기종에 상관없이 실행 할 수 있습니다.

    -. Swing 기반의 Componet를 지원합니다.

    -. Multithreading 기능을 이용하여 동시에 많은 Thread를 발생 시킬수도 있으며 혹은 독립된 Thread를 연속적으로 발생시켜 테스팅 할 수도 있습니다

    -. GUI 환경으로 빠른 작업을 정확하게 할 수 있답니다.

    -. 캐싱된 결과를 분석하고 재현 할 수 있습니다.

    -. 높은 확장성이 있습니다.


    II. 다운로드


    다운 로드는 요기서..

    http://jakarta.apache.org/site/downloads/downloads_jmeter.cgi



    III. 설치 및 실행

     

    설치전 요구사항을 알아봅시다

    먼저 jdk 1.4 이상이어야 합니다. jdk 1.4.1 이하버젼에서는 GUI elements가 잘 작동하지 않는 다는 bug가 레포팅 되었습니다. 운영체제는  unix (solaris, linux) 및  Widnows 계열 모두 잘 작동합니다.


    그럼 다운 받은 파일의 압축을 풀어봅시다. 제가 받은 파일은 2.0.2버젼이군요


    잠깐 디렉토리 구조를 보지요.

    /bin 에는 실행관련 파일들이 들어있습니다.

    /docs 와 /printable_docs에는 사용자 매뉴얼과 데모가 들어있습니다.

    /extras 는 JMeter를 ant로 바로 실행할수 있도록 하는 설명이 들어있고요

    /lib에는 JMeter를 실행하면서 로딩해야할 다른 서드 파티 jar파일을 넣으면 됩니다 만약 JDBC 테스팅을 한다면 해당하는 벤더의 JDBC를 넣으면 되겠네요


    ㅈ ㅏ ~ 그럼 실행해 봅시다 /bin/jmeter.bat 을 따블클릭 합니다.


     

    IV. 테스트 계획 수립

     

    테스트 시작 전 먼저 계획을 수립해봅시다 어떻게 해야 할까요?

    그냥 쉽게 일반적으로 생각해 봅시다

    1) 테스트 인원 및 몇번을 반복할 지 결정한다
    2) 첫화면을 간다
    3) 로그인 페이지로 간다
    4) 500ms 정도 쉰다 (로그인 아이디, 패스워드 입력시간)
    5) 로그인을 한다
    6) 메뉴를 하나씩 클릭한다
    7) 특별한 메뉴는 반복적으로 클릭한다.
    8) 또한 어떤 메뉴는 딱 한번만 클릭해야 한다.
    9) 또한 다른 메뉴는 랜덤하게 클릭한다.
    등의 정도가 되겠지요..

    그럼 이러한 작업들이 모두 JMeter로 가능한가? YES!!



    V. 초간단 테스트 예제

     

    자 그럼 먼저 간단한 예제를 한번 만들어 봅시다.



    먼저 JMeter를 실행하면 다음과 같은 화면이 나올겁니다.

    많이 보고 얼렁 익숙해 집시다.













    왼쪽편에 'Test Plan'이라는 노드가 보이는데 이 노드는 테스트 계획을 나타낼 일련의 실행 코드 집합 이라고 보면 됩니다 (말이 좀 어렵낭?-_-?)


    중간에 User Defined Variables 라고 나오는데 이건 뭔지 저도 잘 모루겠습니다 -_-;

    암튼 분명한건 여기서 정의한 변수 및 값은 테스트 플랜 이하 노드에서 모두 상속받아 사용한다는 겁니다.


    다음에 체크박스 두개가 있는데 첫번째 'Run each Thread..' 요놈은 Thread를 순차적으로 실행 하겠냐 아니면 동시에 실행하겠냐의 여부를 묻는데 체크하지 않으면 동시에 실행 하는걸로 되어 있습니다. 여기서 Thread란 조금 후에 설명 하겠습니다. 두번째 체크박스는 각 요청에대한 서버의 응답 데이터를 파일에 기록할 것인지 여부를 말하는 것인데 이걸 체크하면 성능이 확~ 떨어지니 왠만함 하지 말라고 되어있습니다.


    자 이제 본격적으로 시작해 봅시다. 'Test Plan'을 오른쪽 마우스로 클릭해 봅시다



















    Thread Group 이란 테스트 계획을 수행할 수행 단위 그룹이라 보면 될것 같습니다. 이 Thread Group을 여러개 추가 할 수도 있으며 그럴 경우 위에서 언급한 체크박스로 동시에 실행 할 것인지 순차적으로 실행 할 것인지 결정 해야 합니다.



















    그럼 이름을 뭘로 할까요? 'Jakarta Project'라 하지요 -_-;

    ㅈ ㅏ 이름을 'Jakarta Project'라 하였습니다 멋집니다 ;;


    가운데 체크박스 3개가 있네요

    이건 만약 테스트 수행도중 에러(테스트 대상의 에러)가 발생 한 경우 계속할 것인지 Thread만 멈출 것인지 아니면 테스트 자체를 멈출것인지를 선택합니다 여기선 그냥 'Continue'로 하지요


    그다음 항목이 가장 중요하므로 잘 숙지해야 합니다.

    Number Of Threads 란 이 Thread Group이 생성할 Thread갯수를 말하는데, 쉽게 말하면 이 그룹에서는 몇 사람으로 테스트 할 것이냐 라고 생각하면 됩니다.

    Ramp-Up Period (in seconds)는 한 Thread가 시작 된 후 다음 Thread가 시작 될 때까지의 대기 시간을 의미합니다 예를 들어 보자면 10개의 Thread가 사용되고 Ramp-up Period가 50이라면 총 10개의 Thread가 모두 실행되려면 50초가 걸린다는 겁니다. 즉 평균적으로 보자면 한 Thread가 실행 후 다름 Thread까지 5(50/10)초정도 후에 실행 되다는 거지요 (휴 어렵다 =3)

    Loop Count는 실행 횟수를 말합니다. forever에 체크하면 무한 루프를 돕니다. 이때는 사용자가 수동으로 STOP 시켜줘야 합니다.


    이제 'Jakarta Project'에 마우스를 오른쪽으로 클릭 후 'Add -> Config Element -> HTTP Request Defaults'를 선택합니다.

















    이 'HTTP Request Defaults'는 HTTP request를 설정함에 있어서 이 노드 이하의 HTTP request는 모두 HTTP Request Defaults에서 설정한 값을 디폴트로 상속 받아 사용하겠다는 겁니다.


    반드시 추가해야 할 필수 요소는 아닙니다만 이후의 추가해야 할 HTTP Request노드가 많다면 일일이 다 다음과 같이 세팅해야 함으로 이를 추가해 놓는 편이 좋습니다.


    Protocol에는 당연히 웹이니 HTTP를 써야겠고 Server name or ip에는 연결할 서버의 이름이나 ip를 적습니다. 주의할것은 주소까지 모두 여기에 적으면 안됩니다. 나머지 부분은 path 부분에 적어야 합니다.


    예를들어 naver에서 jsp를 입력하여 찾기하면 다음 주소가 생기는데 이 예를 들어보도록 하지요 http://search.naver.com/search.naver?where=nexearch&query=jsp&frm=t1

    protocol : http

    servarname or ip : search.naver.com

    path : search.naver

    parameter name : where, query, frm

    parameter value : nexearch, jsp, t1

    을 입력하면 OK~


    하지만 위의 설정값 세팅으로 완료된 것이 아닙니다. 이제부터 테스트 실행을 하기 위한

    실질적인 노드를 추가해야 합니다.

    'Jakarta Project'를 오른쪽 마우스로 클릭하여 'Add -> Sampler -> HTTP Request'를 선택합니다.


















    HTTP Request는 HTTP요청을 웹서버에 전송하여 그 결과를 받아오는 Sampler입니다.

    우리는 좀전에 세팅한 HTTP Request Default의 값을 모두 상속하여 사용할 것이기 때문에 여기서 특별히 세팅할 것이 없습니다.


    이제 request에 대한 준비는 되었고.. 이제 그에 대한 응답을 어떻게 분석 할 것인지에 대해 준비를 해 봅시다.


    'Jakarta Project'를 역시나 오른쪽 마우스로 클릭하여 'Add -> Listener -> View Results Tree와 Graph Results'를 추가합니다


    모든게 ㄷ ㅏ ~ 준비되었습니다. 이제 실행해 보도록 하지요!

    이제 상위 메뉴중 'RUN -> START'를 클릭해봅시다.

    그러면 우측 상단에 있던 아주 자그마한 네모박스가 연두색 으로 표시됩니다. 이 표시가 현재 테스트중이라는 표시입니다. Thrad Group에 세팅된 정보에 의해 딱 10번 request가 가게 될 것입니다.



















    먼저 View Result Tree부터 테스트 실행 결과를 봅시다.

    각 HTTP Request 에 대해 여러 정보들을 표시해 주는군요!


    첫번째 탭인 'Sampler result'에는 load time, response code, response header, server, content-type, length등의 정보가 표시된니다.


    두번째 탭인 'Request'에는 request형태가 표시 되는군요

    세번째 텝인 'Response data'에는 응답받은 Html 코드를 보여주며 또한 이를 Render해서 보여줍니다.



















    'Graph Result'를 봅시다. 이 그림은 Thread Group에서 loop count를 forever해서 나타낸 그림입니다. 비쥬얼 하죠?


















    간단하죠? 여기서 배운것을 바탕으로 응용하면 어떠한 테스트 계획이든지 실행 가능 하답니다. 자 다음 시간에는 JMeter의 고급사용법을 알아보도록 하겠습니다.


    =============================================

    본문서는 자유롭게 배포/복사 할수 있지만

    이문서의 저자에 대한 언급을 삭제하시면 안됩니다

    저자 : GoodBug (unicorn@jakartaproject.com)

    최초 : http://www.jakartaproject.com 

    =============================================

    Posted by 1010
    반응형
    jakarta commons DbUtils 간단한 사용법

    Commons DbUtils : 데이터베이스 사용에 있어서 단순노가다로 이루어지던 많은 작업을 편리하게 해준다. 그동안 "이거 귀찮은데 유틸로 뽑아놓을까?" 아니면 "우씨~ 이런 노가다" 하던 부분이 한방에 해결됐다. 단순한 유틸 패키지이기에 사용법도 간단하고 편리하다.


    //1. JDBC 드라이버 로딩을 간략화(로딩 성공시 true 반환)

            if (!DbUtils.loadDriver("com.mysql.jdbc.Driver")) {

                System.out.println("Failed Loading JDBC Driver!");
                System.exit(0);

            }


    //2. Connection, ResultSet, Statement 등의 close를 간단하게

    (null 확인 - > 예외처리의 과정을 간단하게)
            DbUtils.closeQuietly(connection);


    //3. QueryRunner - 쿼리문 수행

    - SELECT

    query(Connection conn, String sql, ResultSetHandler rsh)

    query(Connection conn, String sql, Object param, ResultSetHandler rsh)

    query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) 등

    param은 PreparedStatement의 ?에 해당 .. 2개 이상은 배열을 만들어서 전달


    - INSERT, UPDATE, DELETE의

    int update(Connection conn, String sql)

    int update(Conneciton conn, String sql, Object param) 등

    executeUpdate()와 사용법 동일


    //4. ResultSetHandler - 빈이나 맵을 만들어서 ResultRet에서 읽어들여 넣어주는 노가다여 안녕~!

    BeanHandler, BeanListHandler, MapHandler, MapListHandler


    예)

    String query = "SELECT * FROM user WHERE name = ?";

    User user = null;

    ResultSetHandler rsh = new BeanHandler(User.class);

    QueryRunner runner = new QueryRunner();

    user = (User) runner.query(conn, query, "홍길동", rsh);


    -----------

    import java.sql.*;
    import org.apache.commons.dbutils.DbUtils;


    public class CommonsDBUtilExample {
        public static void main(String[] args) {
            Connection connection = null;
            String url = "jdbc:mysql://localhost/mysql";
            String user = "root";
            String password = "";

            if (!DbUtils.loadDriver("com.mysql.jdbc.Driver")) {
               System.out.println("Failed Loading JDBC Driver!");
               System.exit(0);
            }

            try {
                connection = DriverManager.getConnection(url, user, password);
                System.out.println("Connection successful!!");
                Statement select = connection.createStatement();
       ResultSet rs = select.executeQuery("select * from user");
       while (rs.next()) {
        System.out.println(rs.getString(1));
       }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                DbUtils.closeQuietly(connection);
            }
        }

    Posted by 1010
    반응형
    개발을 하다보면 간단한 화면 1~2개만 독립적으로 돌아가는 웹어플리케이션을 만들 때도 있습니다. 예를 들면 로그조회 프로그램 같은 것들이죠.

    그런 곳에는 Hibernate나 iBatis를 쓰기에는 너무 거창하다는 느낌이 들기도 합니다. 그렇다고 JDBC로 날코딩하기는 성가실때, 이럴 때는 apache commons DbUtils를 써볼만 합니다.


    JDBC에서 Connection, Statement,ResultSet의 close 글에 나온 것처럼 Connection을 닫는 번거로운 처리가 DbUtils.closeQuietly(con);로 끝나는 것만 해도 상당히 편합니다.


    아래 예제는  DBUtils + JSTL로 간단한 조회화면을 만들어 본 것입니다.

    <%@ page language="java" contentType="text/html; charset=EUC-KR"
        pageEncoding="EUC-KR"%>
    <%@ page import = "java.sql.*" %>
    <%@ page import = "java.util.Properties" %>
    <%@ page import = "org.apache.commons.dbutils.DbUtils" %>
    <%@ page import = "org.apache.commons.dbutils.QueryRunner" %>
    <%@ page import = " org.apache.commons.dbutils.handlers.MapListHandler" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%!
      private static final String SELECT_STMT =
                      "SELECT id, name, email, cell_phone_number FROM quiz_user";
    %>
    <%
     String url = "jdbc:hsqldb:hsql://localhost/sampledb";
     Properties prop = new Properties();
     prop.put("user","sa");  
     prop.put("password","");
     Connection con =  null;
     try{
      Class.forName ("org.hsqldb.jdbcDriver");
      con = DriverManager.getConnection(url,prop);
            QueryRunner runner = new QueryRunner();
            Object resultList = runner.query(con,SELECT_STMT, new MapListHandler());
            request.setAttribute("list",resultList);          
       } catch (SQLException ex) {

          throw new RuntimeException(ex);
       } finally {
         DbUtils.closeQuietly(con);
       }
     %>  
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>사용자</title>
    </head>
    <body>
      <h1>사용자  조회</h1>
      <h2>사용자  목록</h2>
      <table class="list">
       <tr>
        <th>id</th><th>이름</th><th>전화번호</th> <th>이메일</th>   
     </tr>
        <c:forEach var="item" items="${list}" varStatus="status">
     <tr>
      <td>${item.id}</td>
      <td align="center">${item.name}</td>
      <td>${item.cell_phone_number}</td>  
      <td align="center">${item.email}</td>
     </tr>
     </c:forEach>
      </table>
    </body>
    </html>


      몇 년전에 DbUtils와 비슷한 클래스를 만든 적이 있었는데, 그때도 좀 찾아볼 걸 그랬나봅니다. 그러고 보면 저도 apache commons에 이미 있는 것을 많이도 만들어본 삽질의 시간들을 겪었었습니다.  신입 때 commons beanutils하고 commons io에 포함된 것 비슷한 유틸리티 만들어 놓고 혼자서 뿌듯해 했었죠 -_-;


    참고자료

    Commons-DbUtils

    dbutils 활용방법



    출처 : http://benelog.egloos.com/1978345

    Posted by 1010
    반응형
    Spring 프레임워크 참고문서
    Spring 프레임워크 강의
    Spring 프레임워크 기반의 테스트
    Spring 프레임워크 Batch
    Acegi Security 문서
    Spring 프레임워크 워크북
    Spring 프레임워크 워크북 관련 문서 및 자료들
    Spring 프레임워크 워크북 추가 문서

    Spring 프레임워크는 상당히 방대한 기능을 제공하고 있으며, 빠른 속도로 발전하고 있다. 따라서 한권의 책에서 Spring 프레임워크가 가지고 있는 모든 기능을 다루기는 힘든 것이 사실이다. 따라서 이 위키를 통하여 Spring 프레임워크 워크북에서 다루지 못한 내용들을 하나씩 채워나갈 생각이다.

    독자들 중 Spring 프레임워크의 기능 중 책에서 다루지 않고 있는 기능에 대하여 알고 싶은 부분이 있다면 게시판을 통하여 제안을 해주기 바란다. 시간이 허락하는 한도내에서 문서를 작성하고 정보를 제공할 예정이다.

    Spring 프레임워크 워크북 부연설명
    • 4장-예제 4-12 applicationContext.xml 부연설명 : 4장 Spring JDBC p.244에 있는 (예제 4-12)applicationContext.xml에 대한 부연설명
    • Spring MVC를 활용한 정적인 페이지 개발 : 6장의 Spring MVC를 단계적으로 추가적인 설명과 처음 Spring MVC를 이용하여 구현하고자 하는 개발자에게 도움이 될 만한 내용이 추가된다. 또한 책에서는 메인페이지를 처음으로 구현하고 있는데, 실제 예제소스에서는 이미 완성된 소스를 보이고 있음으로 단계적으로 개발하기에 부족함이 있는 듯 하여 직접 하나씩 구현해 보면서 예제소스를 만들어 가는 것으로 한다.
    Spring 프레임워크 참고자료
    • Spring 프레임워크 참고 문서 : Spring 프레임워크를 공부하기 위한 참고문서를 정리해 놓은 페이지
    • unitils : Spring, Hiberate 프레임워크, Easymock 프레임워크를 Annotation 기반으로 테스트가 가능하도록 지원하는 프레임워크이다.
    Posted by 1010
    반응형
    2006년 한빛 미디어 강의 (Spring Framework)
    2007년 두번째 한빛 미디어 강의 (Spring Framework)
    2007년 한빛 미디어 강의 문서 (Spring Framework)
    2008년 한빛 교육 센터 강의 자료 (Spring Framework)
    4장-예제 4-12 applicationContext.xml 부연설명 (Spring Framework)
    Acegi Security 분석 - Acl (Spring Framework)
    Acegi Security 분석 - Authentication 기본 (Spring Framework)
    Acegi Security 분석 - Authentication과 Authorization에 대한 기본 개념 (Spring Framework)
    Acegi Security 분석 - Authorization 기본 (Spring Framework)
    Acegi Security 분석 - Domain object instance Authorization (Spring Framework)
    Acegi Security 분석 - Servlet Filter 설정 및 기본 메커니즘 (Spring Framework)
    Acegi Security 분석 - Web URL Authorization (Spring Framework)
    N사 신입 사원 교육을 위한 Spring 프레임워크 강의 (Spring Framework)
    ReloadableResourceBundleMessageSource 클래스를 이용하여 MessageSource 자동 로딩하기 (Spring Framework)
    Spring MVC에 HandlerInterceptor 사용하기 (Spring Framework)
    Spring 프레임워크 기반하에서 컴포넌트 관리 전략 (Spring Framework)
    Spring 프레임워크 빈 설정 파일에 대한 기본적인 설명 (Spring Framework)
    Spring 프레임워크 샘플 애플리케이션 (Spring Framework)
    Spring 프레임워크 워크북 2장 1강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 2강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 3강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 4강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 5강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 3장 1강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 3장 2강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 3장 3강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 3장 4강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 6장 1강 - Spring MVC (Spring Framework)
    Spring 프레임워크 워크북 6장 2강 - Spring MVC (Spring Framework)
    Spring 프레임워크 워크북 원고 준비문서 (Spring Framework)
    Spring 프레임워크 워크북을 위한 로드맵 (Spring Framework)
    Spring 프레임워크 워크북을 위한 참고문서 (Spring Framework)
    Spring 프레임워크 참고 문서 (Spring Framework)
    Unitils를 이용한 단위 테스트 (Spring Framework)
    샘플 예제를 위한 환경 세팅시 참고사항 (Spring Framework)
    자바지기 Spring 프레임워크 강의 (Spring Framework)
    Posted by 1010
    반응형
    출처 : http://www.jakartaproject.com/

    Spring 설치- 서론 spring 설치

    서론...

    작년에 한 것도 없고 요즘 재미도 없고 해서

    올해는 spring을 알아보려 합니다.

    아는 것도 없는데 저의 취미생활로 하니까 돌은 던지기 말아주시면 감사합니다. ^^*

    얼마 전에 회사에서 Spring in action 이라는 책을 신청해서 나와서

    책보면서 작성해 보려 합니다.


    spring 설치


    전 myeclipse를 사용합니다.

     Visit http://www.myeclipseide.com/

    여기서 받으시면 됩니다.

    이 툴을 사용하는 이유는 플러그가 자동으로 설치되고

    spring 개발을 쉽게 도와주기 때문입니다.


     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    그림에서 Add Spring Capabilities… 클릭하시면

    (주의 : 반드시 프로젝트를 하나 만들고 해야 합니다.)






































    그림이 나타나고 finish 하죠


    그런 다음 http://www.springframework.org 에서 spring 최신 버젼을 받습니다.

    책에서는 1.2.7 기준이라고 하는데  홈페이지에서는 벌써

    2.0.2 이네요 걍~~~ 2.0.2로 하려 합니다.

    다운을 하려고 보면 파일이 두 입니다.














    파일은 큰것으로 보아 서드파티 라이브러리가 포함된것이고 아래는 코아만 들어 있다고 하네요

    윗것으로 다운 받습니다

    물론 myeclipse에서 1.2 버전 지원하는데 2.0 다운 받아 수동으로 라이브러리 추가하려고 합니다.

    근데 아마도 책은 1.2 버전 기준이기때문에 나중 2.0 사용하지 않을까 하네요 ^^*



    Spring 라이브러리


    Jar 파일

    용도

    의존대상

    Spring-core.jar

    스프링 핵심 컨테이너와 유틸리티

    커먼스 로깅, 선택사항:Log4J

    Spring-aop.jar

    스프링 aop 프레임워크 메타데이터 지원

    Spring-core.jar, AOP 연맹

    선택사항:cglib, 커먼스 어트리뷰츠

    Spring-context.jar

    애플리케이션 컨텍스트, 유효성 검증 프레임워크, 템를릿 지원

    (벨로시티, 프리마커), 리모팅(jax-rpc, hessian, burlap), EJB 지원,

    스케쥴링

    Spring-core.jar

    선택사항:벨로시티,프리마커,javamail,

    Ejb,jax-rpc,hessian, burlap,쿼츠

    Spring-dao.jar

    Jdbc dao 지원 , 트랜잭션 기반구조

    Spring-core.jar

    선택사항:spring-aop.jar, jta

    Spring-orm.jar

    하이버네이트, jdo, 아이바티스를 포함한 orm 프레임워크 지원

    Spring-core.jar

    선택사항:하이버네이트,jdo,아이바티스

    Spring-web.jar

    애플리케이션 컨텍스트 유틸리티, 멀티파트 파일 업로드지원

    Spring-cotext.jar, 서블릿

    선택사항:커먼스 파일업로드,cos

    Spring-webmvc.jar

    스프링 mvc 프레이워크

    Spring-web.jar

    선택사항:jsp,jstl,타일즈,itext, poi

    Spring.jar

    다른 jar 파일들을 포함한 스프링 프레임워크 전체

    위의 모든 사항들을 포함


    사실 나도 모르는 용어들이 많다 사용해본 것도 개정도 있다.

    앞으로 알아가면 재미(?) 같다. ㅎㅎㅎㅎㅎ


    ------------------------------------------------------------------------------------------------------------------------


    Spring 로드존슨이 만든 오픈소스 프레임 워크이며, 그의 책인 Expert one-on-one : j2ee design and development 처음 소개 되었다 한다. 어라... 이책 봤는데..왜 몰랐지…

    책에서 스프링의 원래 이름은 interface21 었다.


    스프링에 대한 감을 잡기 위한 설명은…


    1.경량

    크기와 부하의 측면에서 경량이고 1MB 크기의 jar파일로 배포된다. 그리고 스프링은 침입적이지 않다고 한다. 무슨말인지.. 스프링을 도입한 애플리케이션의 객체가 보통의 경우 스프링의 특정 클래스에 대한 의존성을 갖지 않는다는 의미라고 한다. 그냥 ejb 비해 의존성이 없다는 얘기로 이해하고 넘어가야 겠다


    2.제어역행

    제어역행(IoC, Inversion of Control)이라는 기술을 통해 애플리케이션의 느슨한 결합을 도모한다.

    말은 기본개념은 객체를 생성하거나 찾는 대신, 구현되는 방법을 기술하는 것이다. 컴포넌트와 서비스들을 코드에 직접 연결하지는 않지만, 설정 파일에서 어떤 컴포넌트가 어떤 서비스를 요구하는지를 기술한다. 컨테이너(이 경우, Spring 프레임웍, IOC 컨테이너)는 이 모든 것을 연결한다.


    3.관점지향

    관점지향 프로그래밍(AOP, Aspect-Oriented Programming) 위한 풍부한 지원을 한다. 여기서 관점지향 프로그래밍이란 비즈니스 로직을 프로그램밍하게만 한다는 것이다. 트랜잭션과 시스템 감시같은 것은  관련 모듈을 이용하면 된다.

    http://aopalliance.soureforge.net 참고하면 된다.


    4.컨네이너

    어플리케이션 객체의 생명주기와 설정을 포함하고 관리한다는 점에서 스프링은 일종의 컨테이너이고, 빈을 생성, 빈의 연관 설정등 있다고 한다.


    5.프레임워크

    스프링에서는 파일내에 선언적으로 구성하여 애플리케이션 객체를 생성하며 어플리케이션 로직 개발은 개발자에게 맡기고 이외는 기능은 모듈로서 제공한다.


























    Spring 프레임웍을 구성하는 각 모듈(또는 컴포넌트)은 독립적이거나, 다른 모듈들과 함께 구현된다. 각 컴포넌트의 기능은 다음과 같다.

    • 코어 컨테이너(core container): Spring 프레임웍의 핵심 기능을 제공한다. 코어 컨테이너의 주요 컴포넌트는 BeanFactory(Factory 패턴의 구현)이다. BeanFactoryInversion of Control (IOC) 패턴을 사용하여 애플리케이션의 설정 및 의존성 스팩을 실제 애플리케이션 코드에서 분리시킨다.
    • Spring 컨텍스트(Spring context): Spring 프레임웍에 컨텍스트 정보를 제공하는 설정 파일이다. Spring 컨텍스트에는 JNDI, EJB, 국제화, 밸리데이션, 스케줄링 같은 엔터프라이즈 서비스들이 포함된다.
    • Spring AOP 모듈(Spring AOP): 설정 관리 기능을 통해 aspect 지향 프로그래밍 기능을 Spring 프레임웍과 직접 통합시킨다. 따라서 Spring 프레임웍에서 관리되는 모든 객체에서 AOP가 가능하다. Spring AOP 모듈은 Spring 기반 애플리케이션에서 객체에 트랜잭션 관리 서비스를 제공한다. Spring AOP에서는 EJB 컴포넌트에 의존하지 않고도 선언적 트랜잭션 관리를 애플리케이션과 결합할 수 있다.
    • Spring DAO: Spring JDBC DAO 추상 레이어는 다른 데이터베이스 벤더들의 예외 핸들링과 오류 메시지를 관리하는 중요한 예외 계층을 제공한다. 이 예외 계층은 오류 핸들링을 간소화하고, 예외 코드의 양도 줄여준다. Spring DAO의 JDBC 예외는 일반 DAO 예외 계층에 순응한다.
    • Spring ORM: 프레임웍은 여러 ORM 프레임웍에 플러그인 되어, Object Relational 툴 (JDO, Hibernate, iBatis SQL Map)을 제공한다. 이 모든 것은 Spring의 일반 트랜잭션과 DAO 예외 계층에 순응한다.
    • Spring Web module: 웹 컨텍스트 모듈은 애플리케이션 컨텍스트 모듈의 상단에 구현되어, 웹 기반 애플리케이션에 컨텍스트를 제공한다. Spring 프레임웍은 Jakarta Struts와의 통합을 지원한다. 웹 모듈은 다중 요청을 핸들링하고, 요청 매개변수를 도메인 객체로 바인딩하는 작업을 수월하게 한다.
    • Spring MVC framework: MVC 프레임웍은 완전한 기능을 갖춘 MVC 구현이다. MVC 프레임웍은 전략 인터페이스를 통해 설정할 수 있으며, JSP, Velocity, Tiles, iText, POI 같은 다양한 뷰 기술을 허용한다.

    Pasted from <http://www-128.ibm.com/developerworks/kr/library/wa-spring1/>



    ------------------------------------------------------------------------------------------------------------------------


    먼저 Hello World 라는 예제를 한번 해본다.



    GreetingService.java
    인터페이스

     : 여기에서 인터페이스로 만들어서 구현하는 이유는 특별히 없다.


























    GreeringServiceImpl.java

    : 인터페이스를 구현한 인사말을 출력하는 클래스이다.

    여기서 주목할것은 setGreeting() 있다는 것이다. 소스를 보게되면 setGreeting() 누가 호출했는지 자세히 알아보는것이다 이것이 spring 힘이 아닐까 한다.


















    applicationContext.xml

    : 파일은 spring 컨테이너에게 클래스에 대한 정보와 설정방법을 알려준다.

    <bean> </beam> 내용은  아래의 코드와 같다.


    GreetingServiceImpl greetingService = new GreetingServiceImpl();

    greetingService.setGreeting("Buenos Dias!");





     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    HelloApp.java 메인 클래스

    : BeanFactory 클래스가 스프링 컨테이너이다.












    그림은 출력물이다.


    아주 간단한 예제 프로그램이다. 생각은 spring 힘은 ejb에서 lookup으로 찾아서 실행하는 것이 아니라

    Xml 이용하여 빈들을 실행하고 제어 , 컨트롤을 하는 것이다. 이것이 역행제어를 말하는 같다.

    Ejb보다는 쉽게(?) 있고 Ejb에서 트랜잭션을 보장 해주는 것을 spring에서도 해주고 비즈니스 로직만 프로그래밍하면 이외 기능은 기타의 모듈로서 해결할 있을 거라 생각한다.


    그럼 앞으로 알아야 것은 빈들을 어떻게 컨트롤하는가 spring 다른 기능의 모듈과 어떻게 연결하는가만

    배우면 같다.



    ------------------------------------------------------------------------------------------------------------------------


    Spring EJB에서 구현되는 것을 좀더 쉽게 구현 있다.(spring 다른 것과 결합하여) 제어 역행이라는 개념을 이용하여 인터페이스를 통해 서로 협업하는 간단한 자바 객체들() 사용해 엔터프라이즈 애플리케이션을 개발할 있다. 이들 빈은 런타임에 spring 컨테이너에 의해 함께 묶인다.

    결국 spring 적은 비용으로 느슨하게 결합된 코드를 유지할 있게 해준다.


    spring 제어 역행의 상부에 AOP 제공한다. AOP 애플리케이션 전체에  걸쳐 흩어져 있는 코드를 한곳(aspect) 위치하도록 준다. 런타임에 빈들이 함께 묶이면 애스펙트들이 그에 엮임으로써, 빈은 새로운 작동을 하게 된다.


    엔터프라이즈 개발을 지원하다는 명목하에, spring 다양한 퍼시스턴스 기술과 통합할 있다. JDBC, 하이버네이트, JDO, 어떤 것을 사용하든, 스프링의 DAO 프레임워크는 퍼시스턴스 프레임워크에 있어서 에러 처리와 자원관리를 위한 일관된 모델을 제공함으로써 개발 편의성을 도모한다.


    퍼시스턴스 기술과의 동합을 보와하는것은 spring 트랜잭션 지원 기능이다. EJB 사용하지 않고도 AOP 통해 애플리케이션에 선언적으로 트랜잭션 지원을 추가할수 있다.또한 분산 트랜잭션을 위한 JTA 트랜잭션과의 통합을 포함하여 다양한 트랜잭션을 지원한다.


    Spring 여러지원 가능한 기능으로 메일,EJB, 서비스, JNDI 다양한 J2EE 서비스와의 통합기능도 제공한다. 제어역행을 통해 이들 서비스를 쉽게 설정하며, 애플리케이션 객체에 단순한 인터페이스를 제공한다.


    Spring 프리젠테이션 계층을 우해 다중 뷰기술을 지원한다. 벨로시티나 JSP 같은 프리젠테이션 기술뿐만 아니라 마이크로소프트 엑셀, PDF 파일을 생성할 있다. 프리젠테이션의 상부에는 내장된 MVC 프레임워크가 존재하고 스트럿츠나 웹워크(webwork) 같은 다른 웹프레임워크의 대안을 제시하며, spring 모든 서비스와 좀더 쉽게 통합 있다.


    생각을 덧붙이자면,

    Spring CBD 개발된 컴포넌트인 같다. spring 트랜잭션을 지원하기 위해 AOP 컴포넌트를 붙이고, 데이터 베이스와의 연결은 JDBC, 하이버네이트, JDO 등의 컴포넌트로 사용하면 되고, 프리젠테이션은 태피스트리나 벨로시티 컴포넌트를 이용하면 같다.

    , 내가 구현 가능한 기능이 있으면 가져다가 spring 붙이고 사용하면 가능하다는 뜻이다.

    근데 그러기 위해서는 많은 컴포넌트를 알아야 하고 spring 붙이기 위해서는 붙이는 방법 등을 학습해야 한다. 알고 나면 쉬운 것이지만 처음부터 알기란 매우 힘들 같다.

    만약 이것을 학습하고 있는데 다른 좋은 무엇가가 나타나면 어떻게 되는 것일까…..




    ------------------------------------------------------------------------------------------------------------------------


    Spring에서는 어떤 컴포넌트가 다른 컴포넌트와의 연관관계를 관리할 책임이 없다.

    어떤 컴포넌트에서 다른 컴포넌트를 호출하는 것을 말하는 같다.

    대신, 컨테이너에 의해 컴포넌트 간의 협업을 위한 참조가 주어진다.

    이렇듯  애플리케이션 컴포넌트 간의 연관관계를 만드는 행위를 묶기(wiring)라고 하며, 이번 장에서 다룰 주제이다.

    Spring 묶기는 단순히 객체간의 연관관계를 성립시키는 작업 이상의 것이다.

    또한 spring 사용해 빈의 특성을 설정하는 방법, 별도 파일로 배치(deployment) 설정을 외부화 하는 방법, 빈의 생명주기를 관리하는 방법 등을 알게 된다.

    비즈니스를 묶는 것과 관련된 내용이 spring 핵심이 아닐까 한다.


    Spring 프레임워크를 사용하기 위해 빈을 설정 때에는, 항상 spring 컨테이너에 어떤 지시사항을 기술해야 한다. 따라서 컨테이너를 알면, 빈이 어떻게 관리되는지를 이해하는데 도움이 되기때문에 컨테이너를 알아보자.

     컨테이너는 spring 프레임워크의 핵심이다. Spring 컨테이너는 제어역행(IOC) 사용해 애플리케이션을 구성하는 컴포넌트를 관리한다. 여기서 협력하는 컴포넌트간의 연관관계를 생성하는 것을 포함한다. 이는 객체는 좀더 명확하게 이행 있고, 재사용이 가능해지며, 단위 테스트가 쉬워진다고 한다.


    Spring 기본 컨테이너 2

       org.springframework.beans.factory.BeanFactory 인터네이스로 정의된 빈팩토리

       : 기본적인 의존성 주입 지원

      org.springframework.context.ApplicationContext 인터네이스로 정의되는 애플리케이션 컨텍스트

       : 프로퍼티 파일의 텍스트 메시지 해석, 애플리케이션 프레임워크 서비스 제공


    외에 다수 존재한다. 그리고 빈팩토리와 애플리케이션 컨텍스트를 모두 컨테이너 용어 한다.


    1. 팩토리

     - 빈을 생성하고 분배하는 책임을 지는 클래스이고 팩토리 디자인 패턴을 구현한 것이다.

        ( 팩토리 패턴은 객체를 생성하고 분배하는 패턴이고 자세한것은 스스로…^^)

     

       빈팩토리는 애플리케이션내의 많은 객체에 대해 알고 있기 때문에 객체들을 인스턴스화할때 협업하는 객체 간의 연관관계를 생성시키는 것이 가능하다.( 말은 객체들간의 관계를 정의하는 무엇가가 필요하는 말이다.)

      이렇게 하는 이유는 자신과 빈의 클라이언트로부터 설정이라는 작업이  없다. 그로 인하여 빈팩토리가 객체를 분배하는 시점에서는 객체들이 완전히 설정된 상태이며, 협업하는 객체들끼리 인식하고 있고 곧바로  사용할 있는 상태인 것이다.  빈팩토리는 커스텀 초기화 메소드와 소멸 메소드를 호출함으로써 빈의 생명주기에 개입할 있다.


    Spring 다양한 BeanFactory 구현 클래스 가장 유용한 것은 org.springframework.beans.factory.xml.XmlBeanFactory 로서 xml 파일에 기술되어 있는 정의를 바탕으로 빈을 로딩한다.


     BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));


    코드는 팩토리에게 XML 파일로부터 빈에 대한 정의를 읽어오라고 알려준다. 그러나 팩토리가 빈을 인스턴스화하는 것은 아니다. 빈은 팩토리에 "늦게 로딩"(lazy loading)되는데, 말은 팩토리가 빈의 정의(빈과 특성에 대한 설명) 즉시 로딩하는 반면, 자체가 필요하기 되기 전까지는 인스턴스화하지 않는다는 의미다.

      팩토리로부터 빈을 언어오기 위해서는 아래와 같다.


      MyBean myBean = (MyBean) factory.getBean("myBean");


    getBean(); 호출되면, 팩토리는 의존성 주입을 이용해 빈을 인스턴스화하고 빈의 특성(빈의 set 메소드) 설정하기 시작한다. 이렇게 해서 스프링컨테이너 안에서의 빈의 일생이 시작된다.


     

    2. 애플리케이션 컨텍스트

     

     표면적으로는 ApplicationContext BeanFactory 상당히 비슷하다. 빈을 로딩하고 빈들을 묶어주며, 요청에 따라 빈을 분배한다. , ApplicationContext 인터페이스가 BeanFactory 인터페이스를 확장한 것이다. 또한 getBean() 메소드를 사용해 ApplicationContext 로부터 빈을 얻을 있다.

    차이점은 팩토리는 모든 빈을 늦게 로딩(lazy loading)하는데, 애플리케이션 컨텍스트는  컨텍스트를 시작시킬때 모든 싱클톤 빈을 미리 로딩(preloading)함으로써 빈이 필요할때 즉시 사용될수 있도록 보장해 준다. , 빈이 생성되기를 기다릴 필요가 없다는 것이다.

    하지만 모든 객체를 싱클톤으로 생성해 놓는다지만 객체가 많아지면 무거울꺼라는 생각이 든다. 해보지 않아서 추측임.

    (싱클톤은 디자인패턴의 일종이다 스스로…. ^^)

     

     ApplicationContext 다음과 같은 추가기능을 제공한다.

     -.국제화(I18N) 지원을 포함해 텍스트 메시지를 해석하는 수단 제공

     -.이미지 등과 같은 자원을 로딩하는 범용적인 방법 제공

     -.리스너로 등록되어있는 빈에 이벤트를 발생할 있음.


     BeanFactory 자원이 제약이 따르는 이동통신기기 같은 상황이 아니면 ApplicationContext 사용한다고 한다.

     

    ApplicationContext 다양한 구현 클래스 일반적인

     -.ClasspathXmlApplicationContext : 클래스 패스에 있는 XML 파일로부터 컨텍스트 정의를 로딩하며, 컨텍스트 정의를 클래스 패스에 있는 자원으로 취급한다.

     -.FileSystemXmlApplicationContext : 파일 시스템에 있는 XML 파일로부터 컨텍스트 정의를 로딩한다.

     -.XmlWebApplicationContext : 애플리케이션에 포함되어있는 XML 파일로부터 컨텍스트 정의를 로딩한다.


    ) ApplicationContext context = new FileSystemXmlApplicationContext("c:/foo.xml");

        : 특정한 위치에서 foo.xml 찾음.

         ApplicationContext context = new ClassPathXmlApplicationContext("c:/foo.xml");

        : 클래스 패스에 있는 foo.xml 찾음.




    ------------------------------------------------------------------------------------------------------------------------


      1. 빈의 일생

      전통적인 자바 애플리케이션에서의 빈의 생명주기는 new 키워드를 통해 인스턴스화 되거나 역직렬화(deserializatino) 되며, 사용할 있는 상태가 된다.




      여기서 잠깐

      직렬화는 객체의 현재 상태(attribute value)를 네트웍상의 다른곳(remote)또는 local의 file등에

      전달/보존하기 위해 byte stream으로 내보내는 작업(상태 copy)을 말합니다.

      예제)

      public class Person implements java.io.Serializable{

          

            public String name = "홍길동";

            public int    age  = "20";

            public static String address = "서울시";

            transient String post ="111";

      }


      위의 예제의 Person이라는 class의 인스턴스가 현재의 상태 를 네트웍으로 전송하기 위해 상태값을 직렬화 하면

      이 직렬화된 데이터는 네트웍으로 전송될것이고, 전달받는 상태를 복원하는 작업을 역직렬화
      (deserialized)라고

      합니다. 물론 전달받는 곳에는 Person이라는 class가 존재해야 합니다.

      그래서 역직렬화하는 과정에서 Person인스턴스가 생성되면서 전달받은 데이터로 상태를 복원합니다.


      RMI(Remote Method Invocation)기술을 사용할 경우 이 직렬화/역직렬화를 통해 remote간에 객체의

      상태를 전달받습니다.


      네트웍 전달뿐 아니라 파일에도 객체의 상태를 저장하기 위해 직렬화와 역직렬화를 사용합니다.

      직렬화는 ObjectOuputStream의 writeObject() 메소드를 사용하며, 역직렬화는 ObjectInputStream의

      readObject()를 사용합니다.


      그리고 직렬화 대상 class는 java.io.Serializable interface를 implements해야 합니다.

      Serializable interface내부는 비어져 있습니다.


      출처 : http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=1117764219&p=3&s=t



      Spring 빈의 생성방법을 커스터마이징 있는 기회를 제공하기 때문에 spring 컨테이너에서의 빈의 생명주기를 이해하는 것이 중요하다.


      다음 그림은 spring 애플리케이션 컨텍스트 안에서의 빈의 생명주기이다.




      빈의 생성


      a. 컨테이너는 빈의 정의(빈에 대한 정보를 어딘가에 있겠죠) 알아낸 다음, 빈을 인스턴스화 한다.

      b. 의존성 주입을 이용해 빈의 정의에 지정되어 있는 모든 특성에 값을 채운다.

      c. 빈이 BeanNameAware 인터페이스를 구현한다면, 팩토리는 빈의 ID setBeanName() 전달하여 호출한다.

      d. 빈이 BeanFactoryAware 인터페이스를 구현한다면, 팩토리는 자신의 인스턴스를 setBeanFactory() 전달하여 호출한다.

      e. 빈이 ApplicationContextAware 인터페이스를 구현했다면 setApplicationContext() 메소드가 호출된다.

      f. 빈과 연관되어 있는 하나 이상의 BeanPostProcessor 존재한다면, 각각의 모든 postProcessBeforeInitialization() 메소드가 호출된다.

      g. 빈이 재정되어 있는 init-method 있으면 호출된다.

      h. 마지막으로 빈과 연관되어 있는 하나 이상의 BeanPostProcessor 존재하다면, 모든 postProcessAfterInitialization()메소드가 호출된다.
      i.
      빈은 애플리케이션에 의해 즉시 사용 있는 상태이며, 이상 필요하지 않을때까지 팩토리 안에 남아 있게 된다.


      빈의 소멸


      a. 빈이 DisposableBean 인터페이스를 구현하고 있다면, destroy() 메소드가 호출된다.

      b. 커스텀 destroy-method 지정되어 있다면 메소드가 호출된다.




    ------------------------------------------------------------------------------------------------------------------------


    Spring 컨테이너 안에서 빈들(컴포넌트)끼리 사용하거나 사용되어지는 것을 묶기라고 한다. 빈을 묶을 에는 어떤 빈들이 사용 지와 빈들을 묶기 위해서 어떻게 의존성 주입을 사용할지를 컨테이너에게 알려줘야 한다. 이때 컨테이너에게 알려 때는 xml 이용하여 알려준다.


    간단한 샘플을 보면서 진행해 보자

    여러분이 스프링 트레이닝 주식회사라는 기술교육단체와 계약된 상태라고 가정하자 스프링 트레이닝사는 수강생이 온라인으로 교과 과정을 등록할 있도록 하는 애플리케이션을 만들어 달라고 요청했다.

    먼저 애플리케이션의 서비스 계층부터 만들어 보자

    아래의 그림은 스프링 트레이닝 애플리케이션에서 서비스 계층을 구성하는 계층을 보여준다.

     


     

    서비스 계층에는 수강생 서비스(Student Service) 교과과정 서비스 (Course Service) 라는 개의 서비스 컴포넌트가 있다.

     -.수강생 서비스(Student Service) : 수강생과 관련된 모든 것을 처리

     -.교과과정 서비스 (Course Service) : 교과과정과 관련된 모든 기능을 처리


    지금은 어떻게 구현되어 있는지 코드를 구현 부분만 살펴보고 나중에

    완전한 소스를 알아보자 궁금하면 www.acornpub.co.kr 에서 소스를 다운 받아 확인해 보세요

     

     

    Student Service.java

     

    public interface StudentService {

      public Student getStudent(String login);

      public void registerStudent(Student student);

      public java.util.Set getCompletedCourses(Student student);

      public String getCurrentUserId();

    }




    CourseService.java

     

     

    public interface CourseService {

      public Course getCourse(int id);

      public Course getCourse(Integer id);

      public void saveCourse(Course course);

      public java.util.Set getAllCourses();

      public void enrollStudentInCourse(Course course, Student student) throws CourseException;

      public void sendCourseEnrollmentReport();

    }



    StudentServiceImpl


     

    public class StudentServiceImpl implements StudentService {

      Logger LOGGER = Logger.getLogger(StudentServiceImpl.class);

     

      private StudentDao studentDao;

     

      // 생성자에 의한 주입

      public StudentServiceImpl(StudentDao dao) {

        studentDao = dao;

      }

      // setter 의한 주입

      // 참고: 소스에는 없다. 다만, 원리를 설명하기 위함.

      public setStudentDao(StudentDao dao) {

        studentDao = dao;

      }



      public Student getStudent(String login) {

        return studentDao.findById(login);

      }


      public void registerStudent(Student student) {

        HashSet authorities = new HashSet();

        authorities.add(new Authority("ROLE_STUDENT"));

        authorities.add(new Authority("ROLE_USER"));

        student.setAuthorities(authorities);


        studentDao.create(student);

      }


      public java.util.Set getCompletedCourses(Student student) {

        return studentDao.getCompletedCourses(student);

      }

     

      /*

       * TODO - This could be used more generically for Instructors as well.

       * But since it's only used for Students right now and because it would

       * be silly to create a UserService for this simple method, we'll just

       * put it here for now.

       */

      public String getCurrentUserId() {

        return ((SecureContext)ContextHolder.getContext()).getAuthentication().getPrincipal().toString();

      }

    }



    StudentServiceImpl 학생을 찾고, 생성하고 수강하는 작업등을 StudentDao 위임하여 StudentDao 데이터베이스와 상호작용하여 처리한다. StudentDao 객체의 실제 구현내용은 나중에 알아보기로 한다.

    그리고 StudentDao 구현한 클래스는 StudentDaoImpl 클래스이다.

    StudentDaoImpl 위의 소스에서 것과 같이 생성자와 setStudentDao() 메소드를 이용하여 StudentDao 대한 참조를 얻을 있다.


    CourseServiceImpl.java

     

     

     

    public class CourseServiceImpl implements CourseService {

     

      private static Logger LOGGER = Logger.getLogger(CourseServiceImpl.class);

      // 생성자 주입을 통한 CourseDao 설정

      public CourseServiceImpl(CourseDao dao) {

        this.courseDao = dao;

      }



      public Course getCourse(int id) {

        return getCourse(new Integer(id));

      }

     

      public Course getCourse(Integer id) {

        return courseDao.findById(id);

      }


      public void saveCourse(Course course) {

        courseDao.save(course);

      }


      public void enrollStudentInCourse(Course course,

          Student student) throws CourseException {


        // TODO:    enforcePrerequisites(course, student);


        // TODO:    Check for schedule conflicts


        course.getStudents().add(student);

        courseDao.save(course);

      }

     

      public Set getAllCourses() {

        return courseDao.findAll();

      }


      public void sendCourseEnrollmentReport() {

        Set courseList = courseDao.findAll();


        SimpleMailMessage message =

            new SimpleMailMessage(this.mailMessage);


        StringBuffer messageText = new StringBuffer();

        messageText.append("Current enrollment data is as follows:\n\n");

       

        for(Iterator iter = courseList.iterator(); iter.hasNext(); ) {

          Course course = (Course) iter.next();

          messageText.append(course.getId() + "    ");

          messageText.append(course.getName() + "    ");

          int enrollment = courseDao.getEnrollment(course);

          messageText.append(enrollment);

        }


        message.setText(messageText.toString());


        try {

          mailSender.send(message);

        } catch (MailException e) {

          LOGGER.error(e.getMessage());

        }

      }

     

      private void enforcePrerequisites(Course course,

          Student student) throws CourseException {

       

        Set completed = studentService.getCompletedCourses(student);


        // Check for prerequesites

        Set prereqs = course.getPrerequisites();

        for(Iterator iter = prereqs.iterator(); iter.hasNext(); ) {

          if(!completed.contains(iter.next())) {

            throw new CourseException("Prerequisites are not met.");

          }

        }

       

        // Check for scheduling clash

        for(Iterator iter = completed.iterator(); iter.hasNext(); ) {

          Course completedCourse = (Course) iter.next();

         

        }

      }

     

      // COLLABORATORS

      private CourseDao courseDao;

      public void setCourseDao(CourseDao courseDao) {

        this.courseDao = courseDao;

      }

     

      private StudentService studentService;

      public void setStudentService(StudentService service) {

        this.studentService = service;

      }


      private MailSender mailSender;

      public void setMailSender(MailSender mailSender) {

        this.mailSender = mailSender;

      }

     

      private SimpleMailMessage mailMessage;

      public void setMailMessage(SimpleMailMessage mailMessage) {

        this.mailMessage = mailMessage;

      }

    }



    CourseServiceImpl 생성자를 통해 CourseDao 참조를 있다. enrollStudentInCourse() 메소드는 수강생을 교과과정에 추가하기 전에 enforcePrerequisites() 먼저 호출한다. 만약 수강생이 선수과목을 이수하지 않았다면 enforcePrerequisites() CourseException 던질 것이며, 이를 다시 enrollStudentInCourse() 던질 것이다.

    enforcePrerequisites() StudentService 참조를 사용해 수강생이 수료한 모든 과목을 얻어온다. 이는 선수과목이라는 비즈니스 요구사항을 충목시키기 위해 CourseDao 뿐만 아니라, CourseServiceImpl과도 참조한다는 의미다.

    CourseServiceImpl CourseDao 생성자를 통해 얻는 것과 달리, StudentService 참조는 setStudentService() 메소드를 통해 얻는다. 이런 이유는 courseDao 이용하여 과목을 찾는데 사용되기 되고,  속성 값을 설정하지 않으면 인스턴스를 생성할수 없기때문에  private CourseDao courseDao 속성을 설정했다.

    다음은 xml 빈을 묶는 것에 대해 알아 보자


    Posted by 1010
    98..Etc/Etc...2008. 8. 18. 10:55
    반응형
    설치형 블로그 Text Cube 설치 하기

    작성자 : 김문규 (MyMK)
    최초 작성일  : 2008. 6.10

    언제부턴가 블로그는 이미 우리 주위에 깊숙히 자리잡은 하나의 트렌드처럼 보인다. 하지만 너무나 익숙해져버린 탓에 네XX, 티XXX, 다X에 하나씩 둥지를 틀고 모두가 비슷한 모양의 블로그에 서로 경쟁하듯이 퍼가기를 일삼고 있다. 그 결과 정보가 생산되는 참여의 공간이 아니라 단지 바이러스처럼 퍼지기만하는 해적판 CD 시장 같은 생각이 든다.

    꼭있다!! 이런 군상에서 고고한 학처럼 살고자 하는 사람들... 그래서 적지 않게 설치형 개인 블로그를 운영하는 사람들이 늘고 있다. 프로그래밍 실력만 있다면 자유로운 스킨 변환, 업로드 공간의 자유로움, 특수 기능의 매쉬업 등이 기존의 대형 블로그들과는 차이점이 있기 때문이다.

    그래서, 회사에서 확인해 볼 사항도 있고하여 설치형 블로그의 대표 선수인 TextCube를 설치해 보았다. 잘 아시겠지만 이전에는 Tatter Tools(태터툴즈)라는 이름으로 유명했던 웹 블로그 플랫폼이다. 또한 다음과 제휴하여 제공되고 있는 Tistory의 기반 플랫폼이기도 하다. (대단한 녀석이지만 Tistory에 적용되면서 설치형 블로그로서의 장점이 많이 사라지기도 했다.)

    아마도 많은 분들이 비슷한 작업을 시도할 것으로 생각되기에 설치 시에 삽질을 줄이기 위해 성공한 방법을 정리해서 공유하고자 한다. 남는 역량을 대한민국 IT 발전을 위해 써주길 바란다. ^^

    그럼 시작해 보겠다.

    일단 TextCube의 수많은 장점은 아래의 링크를 통해 확인하길 바란다.
    http://textcube.org/center/entry/textcube-1-7-sneak-peak

    1. 웹 호스팅 서버에 설치하기
    http://textcube.org/manual/entry/installing-textcube?category=5
    참조 바람~

    2. 본인의 PC에 설치하기
    (1) APM_Setup을 설치한다.
    http://www.apmsetup.com/
    윈도우 경우에는 이것을 설치하면 아무런 문제가 되지 않는다.
    리눅스의 경우에는 mod_rewrite를 활성화 시켜서 apache를 재컴파일 해야 한다. (필요시 해당 부분 검색!)

    (2) 소스 설치
    textcube-1.x.x-expansion의 압축을 풀어 tc 폴더 아래의 내용을 APMServer/htdocs 아래에 풉니다.
    디렉토리를 구분해서 다중 설치할 수 있어 보입니다. 그런데 설정에 미숙해서 인지 비정상적으로 설치됩니다. 아래와 같이 설치 후 플랫폼 설정 시 다중 사용자가 지원되니 큰 문제가 되지 않습니다.
    사용자 삽입 이미지

    (3) TextCube 설정하기
     -
    http://(ip_address)/setup.php 접속
     
     - (1단계) 한국어 선택
    사용자 삽입 이미지

     - (2단계) '새로운 텍스트큐브 설정합니다' 선택
    사용자 삽입 이미지
     
     - (3단계) phpMyAdmin을 이용해서 db생성 후, 그림과 같이 설정
    사용자 삽입 이미지

    사용자 삽입 이미지

     - (4단계) 아래와 같이 모두 OK가 떠야 함. 빨간색 글씨가 보이면 해결해야 함. (어려움 ^^)
    사용자 삽입 이미지
     
     - (5단계) '하위 경로로 블로그 식별' 선택 (단일 사용자의 경우에도 여러개의 하위 주소로 운영할 수 있으면)
    사용자 삽입 이미지
     
     - (6단계) 적절한 관리자 정보 입력
    사용자 삽입 이미지
     
     - (7단계) 완료
    Posted by 1010
    반응형
     
    사용자 삽입 이미지
    Spring Framework 개요

    작성자 : 김문규
    최초 작성일 : 2008. 6.25


    1. 한마디로
    엔터프라이즈 급의 웹 애플리케이션 개발을 위한 여러 기능(라이브러리)을 제공하는 프레임워크입니다.


    2. 특징, 장점
     - Lightweight Container
     - Dependency Injection Pattern 지원
     - AOP(Aspect Oriented Programming) 지원
     - POJO(Plain Old Java Object) 지원
     - 일관된 Transaction 처리 방법 제공
     - 다양한 Persistance 관련 API 제공 (JDBC, iBATIS, Hibernate, JPA, JDO...)
     - Restlet과 연동 가능 (요고는 내 입장에서 특징임)


    3. 관련 Web Site
    http://springframework.org/


    4. 맺음말
    Spring 프레임워크는 최근 가장 많이 사용되고 있는 웹 프레임워크 입니다. 제공하는 기능이 방대하면서도 그 사용은 용이하기 때문입니다. Spring이 제공하는 기능 중에 가장 강력한 기능은 물론 IoC(DI)와 AOP 입니다. 이는 이어지는 포스트에서 아주 자세하게 다루어질 예정입니다.

    그리고 최근 주목받고 있는 REST 아키텍쳐의 자바 구현체인 Restlet과 같이 사용할 수 있기도 합니다. Restlet 만으로는 모든 웹서비스를 구현하기에는 불편한 것이 사실입니다. 하지만 다행이도 Spring과 결합 모델로 구현이 가능하다고 합니다. (이 부분은 관련 포스트에서 다룰 수 있을지 잘 모르겠습니다.)

    프레임워크라는 것은 일종의 트렌드입니다. 최근에 가장 인기 있는 프레임워크에 대해서 공부해 두는 것 쯤은 개발자에게 도움이 되지 않을 까 생각됩니다. 이후의 포스트들이 지적 호기심 충족에 도움이 되길 바랍니다.

    그럼 이만! 쓩!

    -----------------------------------------------------------------------------------------------------


    Inversion of Control in Spring Framework

    작성자 : 김문규 (MyMK)
    최초 작성일 : 2008. 7.10

    IoC는 Spring Framework의 장점을 꼽으라면 가장 먼저 언급되는 개념입니다. 한국어로 변역하면 '제어의 역행'! 한국어가 더 어려워 보입니다. 비슷한 말로 Dependency Injection(DI)라는 말고 있습니다. 한국말로는 의존성 삽입! 아하~ 조금 이해가 되시나요?

    간단하게 이해하기 쉽게 같이 알아보지요.

    1. 개념
     객체 간의 의존관계를 객체 내부에 선언 또는 정의하지 않고, 외부의 조립기를 이용하여 의존 관계를 설명한다는 것

    2. 예제
    객체간의 연관성을 선언하는 3가지 방법을 보고, 문제점이 어떻게 IoC(DI)를 이용해서 해결되는 지 알아보지요.

    1) 직접 선언하는 방법

    public class WriteArticleServiceImpl {
        private ArticleDao articleDao = new MysqlArticleDao();
        ...
    }

     - 쉽습니다.
     - 하지만, 이를 테스트하기 위해서는 MySqlArticleDao가 정상적으로 동작해야 합니다. 어렵습니다.
     - 또한, OracleArticleDao를 사용하기로 바뀌었다면 코드를 고쳐야 하지요~ 물론 컴파일도 다시요. 귀찮습니다.

    2) Factory 패턴, JNDI를 이용하는 방법

    public class WriteArticleServiceImpl {
        private ArticleDao articleDao = ArticleDaoFactory.create();
        ...
    }

     - 조금 나아졌나요? 최소한 Oracle로 바뀌어도 코드 수정은 안해도 되겠네요~ ^^
     - 근데 테스트 측면에서는 전혀! 나아진게 없어 보입니다. 올바르게 동작하는 Factory와 JNDI에 등록된 객체가 필요합니다.

    3) 외부 조립자를 이용하는 방법

    public class WriteArticleServiceImpl {
        private ArticleDao articleDao;
        public WriteArticleServiceImpl(ArticleDao articleDao) {
            this.articleDao = articleDao;
        }
        ...
    }

    외부 설정 파일 (applicationContext.xml)
    <bean name="writeArticleService" class="com.sec.service.WriteArticleServiceImpl">
        <constructor-arg><ref-bean="articleDao" /></constructor-arg>
    </bean>

     - 외부 설정(applicationContext.xml)에서 객체간의 의존성을 설명하고 있다는 감이 오시지요? 바로 이겁니다. 외부에서 객체 의존성을 정의하고 있는 것이지요. 책에서는 조립한다고 설명하더군요. (Nice!)
     - 여기서는 생성자를 이용한 방법을 사용하지만 setter를 이용하는 방법도 있습니다. 요건 나중에 차차..
     - 이제 위에서 말한 2가지 문제점이 다 해결되어 보이지요? 아하~ 굳입니다. ^^

    3. 참조
    1) 웹 개발자를 위한 스프링 2.5 프로그래밍
    2)
    http://martinfowler.com/articles/injection.html
     - 다음은 IoC 설명의 정석으로 불려지는 Martin Fowler의 글입니다. 시간이 되시면 읽어보세요. 위의 설명이 좀 더 잘 이해되실 겁니다.


    -----------------------------------------------------------------------------------------------

    Aspect Oriented Programming (AOP) in Spring Framework

    작성자 : 김문규
    최초 작성일 : 2008. 7.10

    1. 정의
    AOP는 Spring Framework의 중요한 특징 중 하나입니다.
    AOP란, 기능을 핵심 비지니스 로직과과 공통 모듈로 구분하고, 핵심 로직에 영향을 미치지 않고 사이사이에 공통 모듈을 효과적으로 잘 끼워넣도록 하는 개발 방법입니다.
    공통 모듈은 보안 인증, 로깅 같은 요소들이 해당됩니다.

    예를 들어 다시 설명하면, 로그를 찍기위해 로그 출력 모듈을 만들어서 직접 코드 사이사이에 집어 넣을 수 있겠지요? 이런건 AOP 적이지 않은 개발입니다.
    반면에 로그 출력 모듈을 만든 후에 코드 밖에서 이 모듈을 비지니스 로직에 삽입하는 것이 바로 AOP 적인 개발입니다. 코드 밖에서 설정된다는 것이 핵심입니다.

    2. 예제
    1) AOP스럽지 못한 코드

    public class InventoryController implements Controller {
        protected final Log logger = LogFactory.getLog(getClass());
        private ProductManager productManager;
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String now = (new java.util.Date()).toString();
            logger.info("returning hello view with " + now);
            Map<String, Object> myModel = new HashMap<String, Object>();
            myModel.put("now", now);
            myModel.put("products", this.productManager.getProducts());
            return new ModelAndView("hello", "model", myModel);
        }
        public void setProductManager(ProductManager productManager) {
            this.productManager = productManager;
        }
    }

    로그를 찍고 싶은 지점에 로깅 코드를 직접 삽입하는 방법입니다. 물론 이렇게 하는 것이 효율적일 수도 있습니다. 하지만, 클래스 진입 시점마다 로그를 찍는 것과 같이 동일한 패턴이 있는 경우에는 rule을 정의하고 여기에 따라서 동일한 모듈이 호출된다고 하면 매우 직관적이고 간결하면서 유지 보수가 편하게 구현이 될것으로 생각됩니다. 이것을 지원하는 것이 바로  AOP이며 spring에서는 이를 지원하고 있습니다.

    2) Spring에서 AOP를 사용하는 방법
    Spring에서는 크게
     - Spring API를 이용하는 방법
     - XML schema을 이용하는 방법
     - Annotation 기능을 이용한 방법
    이 있습니다.
    여기서는 2번째 XML schema를 이용하는 방법의 예제를 소개합니다.
    전체 소스를 첨부합니다.


    loggingaspect.java

    package springapp.common;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.aspectj.lang.JoinPoint;
    public class LoggingAspect {
     protected final Log logger = LogFactory.getLog(getClass());
    // pointcut method 호출 전에 실행 시킬 로깅 함수
    public String beforeLogging(JoinPoint joinPoint) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("calling: " + methodName);
      return methodName;
     }

    // pointcut method 결과 리턴 후에 실행 시킬 로깅 함수
     public void returningLogging(JoinPoint joinPoint, Object ret) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("called successfully: " + methodName + " returns " + ret);
     }

    // pointcut method에서 예외 발생시에 실행 시킬 로깅 함수
     public void throwingLogging(JoinPoint joinPoint, Throwable ex) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("exception occured: " + methodName + " throws "
         + ex.getClass().getName());
     }

    // pointcut method 종료 후에 실행 시킬 로깅 함수
     public void afterLogging(JoinPoint joinPoint) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("finish call: " + methodName);
     }
    }


    applicationContext.xml

    .... (다른 설정 생략)

    <bean id="logging" class="springapp.common.LoggingAspect" />
     
    <aop:config>
      <aop:pointcut id="publicMethod" expression="execution(public * springapp.service..*.*(..))" />
      <aop:aspect id="loggingAspect" ref="logging">
          <aop:before pointcut-ref="publicMethod" method="beforeLogging" />
          <aop:after-returning pointcut-ref="publicMethod" method="returningLogging" returning="ret" />
          <aop:after-throwing pointcut-ref="publicMethod" method="throwingLogging" throwing="ex" />
          <aop:after pointcut-ref="publicMethod" method="afterLogging" />
      </aop:aspect>
    </aop:config>

    .... (다른 설정 생략)


    먼저 AOP 관련 용어를 설명하도록 하겠습니다.
     - advice : 언제 어떤 기능을 적용할 지에 대한 정의
     - joinpoint : 공통 기능 적용 가능 지점 (spring에서는 메서드 호출만이 가능합니다.)
     - pointcut : joinpoint 중에 실제로 적용할 지점
     - weaving : 핵심 로직에 공통 로직을 삽입하는
     - aspect : 여러 객체에 공통으로 적용되는 공통 관심 사항

    설정 정보는 아래의 구조로 생성합니다.
    <aop:config>
        <aop:aspect> : aspect를 설정
            <aop:before> : method 실행 전
            <aop:after-returning> : method 정상 실행 후
            <aop:after-throwing> : method 예외 발생 시
            <aop:after> : method 실행 후 (예외 발생 예부 상관 없음)
            <aop:around> : 모든 시점 적용 가능
        <aop:pointcut> : pointcut 설정

    pointcut 설정은 AspectJ의 표현식에 따릅니다.

    execution( 수식어패턴 리턴타입패턴 패키지패턴.메소드이름패턴(파라미터패턴) )


    이제 모든 설정은 끝났습니다. 그닥 힘들이지 않고 설정할 수 있습니다. 하지만 joinpoint에 해당되는 지점만 적용이 가능하기 때문에 사용해야 할 시점을 잘 선택해야 할 듯 합니다.

    3. 맺음말
    지금까지 필터같은 기술을 이용해서 이와 비슷한 기능을 구현 했었지요. 실제 대형 과제를 할 경우에는 더욱더 필요한 기능이 아닌가 싶습니다. 아무리 모듈화를 하고 그 모듈을 적용한다 하더라도 일일이 코드에 적용하는 것은 정말 귀찮은 일일뿐 아니라 오류의 소지도 많아지게 됩니다. 그렇기 때문에 AOP는 정말 유용한 기능이 아닐 수 없습니다. 개인적인 생각으로는 이거 하나만으로도 spring을 써야할 이유가 아닐까 합니다. 제가 드린 예제를 기반으로 꼭 여러분의 과제에 적용해 보시길 바랍니다. 감사합니다.

    4. 참조
    마지막으로 AOP를 이해하는 것에 많은 도움을 준 칼럼을 소개하고자 합니다. 한빛미디어에 김대곤님이 기고하신 글입니다.

    less (닫기)..

    AOP 개요

    저자:
    김대곤(private@roadtohome.com)

    본 기사는 Aspect Oriented Programming에 대해 간략한 소개글이다. 아직까지는 생소한 분야일 수 있겠지만, 점점 더 많이 듣게 되리라 생각된다. AOP를 설명하는데 있어서 자주 등장하는 네 개의 용어들(Aspect, Cross-cutting concern, Point-cut, Advice)를 설명함으로서 AOP가 왜 등장하게 되었으며, AOP가 제시하는 해결책에 대해 살펴볼 것이다. 먼저 "Aspect", "Oriented", "Programming"에서 생소한 단어는 단연 "Aspect"일 것이다. 야후 사전의 정의에 따르면, "Aspect"은 "사물의 면, 국면, 관점"으로 정의되어 있다. 소프트웨어 시스템은 여러가지 관점에서 바라볼 수 있다, 또는 여러 가지 단면을 가지고 있고 있다. 예를 들어, 자금 이체를 하는 프로그램을 작성한다고 생각해 보자. 출금계좌와 입금계좌, 그리고 이체금액을 입력받아 SQL 문장 또는 함수 한 번 돌리는 것으로 끝나는가? 절대 아니다. 먼저, 해킹을 방지하기 위해 사용자가 적절한 보안 프로그램을 설치했는지 점검하는 코드도 있어야 하고, 사용자가 인증되었는지 점검하는 코드도 써야 하고, 상대방 은행에서 적절하게 처리되었는지도 점점해야 하고, 혹시 사용자가 이체버튼을 두 번 누른 것은 아닌가 체크해야 하고, 시스템 로그도 남겨야 한다. 즉, 구현하려고 하는 기능 뿐 아니라 보안, 인증, 로그, 성능와 같은 다른 기능들도 녹아 있어야 한다. 어쩌면 이체를 위한 코드보다 잡다한 다른 측면의 문제들을 다루는 코드가 더 길어질 수 있다. 이런 코드들은 입금이나 출금 같은 다른 곳에서 들어가야 한다. 구현하려고 하는 비즈니스 기능들을 Primary(Core) Concern, 보안, 로그, 인증과 같이 시스템 전반적으로 산재된 기능들을 Cross-cutting concern이라고 부른다. AOP는 Cross-cutting concern를 어떻게 다룰 것인가에 대한 새로운 패러다임이라고 할 수 있다.

    AOP는 구조적 방법론에서 객체지향 방법론으로 전환처럼 시스템 개발에 관한 전체적인 변화는 아니다. Object-Oriented Programming이 Aspect-Oriented Programming으로 대체되는 일은 없을 것이다. AOP는 구조적 방법론에도 적용될 수 있고, 다른 방법론에도 다 적용될 수 있지만, 주로 객체지향방법론이 가지는 단점을 보완하는 것으로 묘사되고 있다. 그러면 객체지향 프로그래밍이 또는 다른 이전의 프로그래밍 기법들이 Cross-cutting Concern를 어떻게 다루는지 알아보자. 매우 간단하다. Primary Concern를 구현한 프로그램에 함께 포함시켰다. 그것이 단 한 줄의 메소드 호출이라 하더라도. 많은 프로그래머들은 거의 모든 프로그램에 산재된 로그하는 단 한 줄의 코드를 찾아서 바꾸어 본 경험이 있을 것이다. 또는 간단하게 생각하고 프로그램을 수정하려고 했는데, 도데체 어디를 수정해야 되는지 모르게 코드가 길고, 알 수 없는 코드들이 자리를 차지하고 있을 때의 난감함. Primary concern, Cross-cutting concern이 하나의 프로그램 안에 들어가게 되면, 프로그램을 이해하기가 힘들고, Cross-cutting concern 코드가 여기저기에 산재되어 수정하기 힘들게 된다. 당연히 생산성 떨어지고, 품질 떨어지고, 유지보수 비용 많이 들게 된다.

    그럼 AOP는 Cross-cutting concern를 어떻게 처리하는가? 이것도 매우 간단하다. 새로운 아이디어라고 할 수도 없다. Primary Concern 구현하는 코드 따로, Cross-cutting concern 구현하는 코드 따로 쓰고, 나중에 두 개 조합하게 완벽한 어플리케이션 만들겠다는 것이다. 기술 용어로 쓰면, Advice(Cross-cutting concern 구현한 코드)와 Primary concern 구현한 코드를 Point-cut 정보를 이용해서 Weaving(조합)하는 것이 AOP가 이 문제를 다루는 방법이다.

    사용자 삽입 이미지


    기술적 용어로서의 "Aspect"은 "Advice"와 "Point-cut"을 함께 지칭하는 단어이다. Point-cut은 어떤 Advice를 Code 어느 위치에 둘 것인가 하는 것이다. 예를 들면, 로그 기능을 구현한 Advice는 Code 속에 있는 모든 public 메소드가 수행되고 나면, 그 마지막에 실행되어라 라고 지정한 것이라 할 수 있다.

    이전까지의 객체지향 프로그래밍은 Cross-cutting concern을 정적으로 어플리케이션에 결합시킨 반면 AOP는 동적으로 Cross-cutting concern를 다룬다고 표현하기도 합니다. 용어에서도 알 수 있듯이 AOP는 소프트웨어 엔지니어링 원칙 중에 하나인 "Separation of concern"를 구현하려고 하고 있습니다. 이러한 문제들을 다루고 있는 분야 중에 하나는 디자인 패턴할 수 있고, 예를 들어, Visitor 패턴은 정적인 구조를 동적으로 바꾸려고 합니다. AOP가 현재까지 나온 방법들 중에서 Cross-cutting concern를 다루는 가장 좋은 방법인가 하는 질문엔 아직 답하긴 힘들 것 같습니다. 그럼에도 분명 언제가는 책상 위에 관련 서적 한 권 있어야 할 것 같은 분야가 될 것 같습니다.

    출처 : http://network.hanb.co.kr/view.php?bi_id=968


    --------------------------------------------------------------------------------------------------
    Developing a Spring Framework MVC application step-by-step

    작성자 : 김문규 (MyMK)
    최초 작성일 : 2008.07.22

    다음의 내용은 spring framework 공식 홈페이지(www.springframework.org)의 튜토리얼을 정리한 것입니다. 저와 같은 spring 초보자를 위해서 개인적인 방식으로 재구성하였습니다. 처음 spring을 접하는 분들이 학습에 참고 자료로 활용하시길 바랍니다.  

    코드 자체의 세세한 부분은 다루지 않을 것입니다. spring 프레임워크을 이용해서 기본적인 웹 서비스를 구현하는 방법을 보여주는 것에 주력할 것입니다. 따라서, 이 글에서는 설정 파일의 작성에 대해서 중점적으로 논의하겠습니다.

    (이 글은 기본적인 자바 웹 개발에 경험이 있는 분을 기준으로 작성합니다.)

    1. Source Structure


    사용자 삽입 이미지

    튜토리얼치고는 소스와 설정파일이 많은 편입니다. 이는 Spring 자체가 MVC를 지원하는 프레임워크이고 설정 파일도 논리적으로 분리하는 것을 권장하기 때문입니다. 튜토리얼에서도 이 개념은 유지하고 있습니다. 이는 차차 설명하기로 합니다.

    1) 소스 구조
     최상위 폴더는 아래와 같습니다.
    ┌ bin - 컴파일된 class 저장
    ├ db - DB와 관련된 스크립트
    ├ src - 말 그대로 소스들
    └ war - WAS에 deploy될 것들
    을 가리킵니다.

    각각의 폴더에 대해 조금 더 자세히 알아보도록 하겠습니다. 조금 생소한 부분만 설명합니다. 너무 쉬운 부분은 넘어가도록 하겠습니다.

    /src
    springapp은 최상위 package이고 여기에 domain, repository, service, web이라는 하위 package가 존재합니다. 아래의 기준으로 클래스 모듈을 위치 시킵니다.
    . domain - data 모델링 객체
    . repository - DAO 과 관련된 객체
    . service - 비지니스 로직
    . web - web 프리젠테이션 관련 객체

    □ /war
    . /WEB-INF/tld/spring-form.tld
    spring에서 사용할 수 있는 tag library의 정의입니다. tag library는 기존 jsp에서 사용하던 tag를 spring에서 재정의 한 것으로 좀 더 사용이 편합니다.
    . /WEB-INF/*.xml
    설정 파일들입니다. 실질적으로 spring을 사용함에 있어 가장 먼저 이해하여야 할 부분입니다. 뒤에서 자세히 설명하도록 하겠습니다.




















    2. Configuration 관련
    1) web.xml
      □ org.springframework.web.context.ContextLoaderListener
         . 계층별로 나눈 xml 설정파일을 web.xml에서 load되도록 등록할 때 사용.
         . 기본값으로 applicationContext.xml을 사용함
       예시)
       <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
       </listener>
       <context-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>
                  /WEB-INF/mars-ibatis.xml
                  /WEB-INF/mars-service.xml
             </param-value>
       </context-param>

      □ <taglib>
         . spring tag library의 위치 설정

    2) applicationContext.xml
      □ ContextLoaderListener(또는 ContextLoaderServlet)가 ApplicationContext를 만들 때 사용

      □ ApplicationContext란, 웹에 독립적인 애플리케이션의 모든 영역(dao, service, manager, 기타 등등) 에 대한 정의를 말합니다.

      □ connection pool에 대한 정의
         . org.apache.commons.dbcp.BasicDataSource
            : datasource를 정의, db driver, 보안 정보등을 알림
         . org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
            : 읽어들일 property 파일에 대한 정보를 알림

      □ transaction에 대한 지원을 위한 한 방법
      이 부분은 나를 비롯한 spring 초보자의 입장에서 아직 이해하기 어려운 부분이기 때문에 아래의 코드가 AOP 기법을 활용하여 transaction을 지원하고 있다는 것만 알아두기로 하자.

    transaction 지원 코드 보기


    3) springapp-servlet.xml
      □ DispatcherServlet이 WebApplicationContext를 만들 때 사용

      □ WebApplicationContext이란, 특정 웹(DispatcherServlet)에 종속되는 영역(Controller 및 각종 웹관련 설정 bean들)에 대한 정의를 말합니다. (ApplicationContext과의 차이를 확인하세요.)

      □ 주로 웹 페이지 리소스에 대한 핸들러 클래스 빈의 정의가 존재합니다.

      □ org.springframework.context.support.ResourceBundleMessageSource
         . message.properties 파일에 정의된 속성 값을 사용할 수 있도록 함
         . 국제화에 응용되면 아주 좋아 보입니다~

      □ org.springframework.web.servlet.view.InternalResourceViewResolver
         . prefix와 suffix 값의 정의에 따라 리소스 표현을 축약할 수 있도록 함.
         예시)
        <bean id="viewResolver"
         class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass"
              value="org.springframework.web.servlet.view.JstlView"></property>
            <property name="prefix" value="/WEB-INF/jsp/"></property>
            <property name="suffix" value=".jsp"></property>       
        </bean>
         라고 선언하면 /WEB-INF/jsp/testfile.jsp은 비지니스 로직 내부에서 testfile로 간단하게 표현 가능 함



    3. Inside Code
    spring web service는 아래의 흐름을 가집니다.
    ①(사용자) jsp 호출
    ②(웹서버) 설정 검색
    ③(웹서버) 요청 처리 클래스 호출
    ④(웹서버) 비지니스 로직 수행
    ⑤(웹서버) view로 결과 반환
    ⑥(사용자) jsp 화면


    1) Controller
      □ 사용자에게 jsp가 호출되면 xxxxx-servlet.xml에 정의된 클래스 빈을 호출합니다.

      □ 예제에는 DB에 있는 정보를 가져다가 저장하는 페이지와 원하는 값을 저장하는 페이지 두가지가 있습니다. hello.jsp가 전자에 해당하고 priceincrease.jsp는 후자에 해당합니다. 각 페이지를 확인하시고 각 페이지의 핸들러 클래스 빈을 springapp-servlet.xml에서 추적해 보시길 바랍니다.

      □ 일반적인 controller의 사용 예 : springapp.web.InventoryController.java
         . 일단 기본적인 Controller의 예시로 Controller interface를 구현합니다. 대부분의 경우에 사용하면 되고, request에 대한 처리를 위해 handleRequest()로 진입합니다.
         . 여기서 비지니스 로직을 처리한 후, ModelAndView 객체를 반환합니다. 이 때 넘기고자 하는 데이터를 파라미터에 담아서 보낼 수 있습니다.

        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String now = (new java.util.Date()).toString();
            logger.info("returning hello view with " + now);
            Map<String, Object> myModel = new HashMap<String, Object>();
            myModel.put("now", now);
            myModel.put("products", this.productManager.getProducts());
            return new ModelAndView("hello", "model", myModel);
        }

      □ 폼을 처리하는 controller의 예 : springapp.web.PriceIncreaseFormController.java
         .  jsp가 폼을 다루고 있을 경우에는 SimpleFormController Class를 상속하여 확장합니다.
         . onSubmit()은 jsp에서 버튼이 눌려진 경우 콜백 지점을 가리키도 formBacjingObject는 폼에 기본값을 저장하고자 할 때 사용합니다.
         . formBackngObject는 domain에 존재하는 모델링 객체를 이용하고 있음에 주의합니다.

        public ModelAndView onSubmit(Object command)
                throws ServletException {
            int increase = ((PriceIncrease) command).getPercentage();
            logger.info("Increasing prices by " + increase + "%.");
            productManager.increasePrice(increase);
            logger.info("returning from PriceIncreaseForm view to " + getSuccessView());
            return new ModelAndView(new RedirectView(getSuccessView()));
        }
        protected Object formBackingObject(HttpServletRequest request) throws ServletException {
            PriceIncrease priceIncrease = new PriceIncrease();
            priceIncrease.setPercentage(20);
            return priceIncrease;
        }

    2) Model
      □ 비지니스 로직을 수행 중에 데이터를 읽고 쓰고 수정하고 삭제하기 위해 데이터 베이스에 대한 접근을 수행합니다. 해당 예제는 HSQLDB를 사용하고 있으며 이를 JDBC로 연결하고 있습니다.

      □ JDBC와 관련하여 몇개의 클래스를 추가 지원하고 있지만 미미합니다. 실제로는 iBatis, Hibernate를 지원하고 있다는 것이 더 큰 특징이라 하겠습니다.

      □ 간단한 예제를 확인하시길 바라며, 이 부분은 설명하지 않겠습니다.

    3) View
      □ 예제에서는 JSTL(Java Server page Standard Tag Library)이라는 것을 사용하고 있기에 약간의 공부가 필요합니다.
         . 예를 들면, <input type='text' /> 이렇게 사용하던 것을 <form:input path="percentage"/> 이런식으로 사용합니다.
         . iteration, print 같은 주요 제어문과 폼이 tag로 정리되어 있습니다.
         . 더 자세한 내용은 http://java.sun.com/products/jsp/jstl/ 참조 바랍니다.



    4. Ending
    이 문서의 목적은 spring을 자세하게 설명하는 것이 아니라, 튜토리얼데로 spring MVC를 간단하게 한번 만들어 보는 것에 그 의의를 두고 있습니다. 튜토리얼과의 차별화를 위해 Top-down 방식을 취했으며 전체의 구성을 설명하고자 했습니다.
    저도 약 2주전부터 틈틈히 공부하면서 알게된 것을 정리한 것이라 부족한 면이 많습니다. 시간이 나는데로 잘못된 부분은 수정하고 필요한 내용은 추가하겠습니다.
    긍적적이고 발전적인 커멘트를 기다립니다. 감사합니다.

    ----------------------------------------------------------------------------------------------------
    Persistance Support of Spring Framework

    작성자 : 김문규
    최초 작성일 : 2008. 8. 1

    Spring에서 DB에 연결하는 방법에 대해서 알아보려 합니다. 다음과 같이 세가지 방법이 있습니다.
     - JDBC를 이용하는 방법
     - JdbcDaoSupport 클래스를 이용해서 JDBC를 조금 편하게 템플릿화하는 방법
     - SqlMapClientDaoSupport 클래스를 이용해서 iBatis Framework를 연동하는 방법

    각 방법이 어떻게 다른지를 설명드리고 튜토리얼 수준의 예제를 보여드리겠습니다.

    1. JDBC
    일반적으로 JDBC를 이용할 경우 아래의 순서를 따라서 구현됩니다. 
     - DriverManager에 해당 DBMS Driver를 등록
     - 해당 Driver로 부터 Connection 객체 획득
     - Connection 객체로부터 Statement 객체 획득
     - Statement의 method를 이용하여 SQL실행
     - ResultSet 으로 받아서 처리(executeUpdate 의 경우엔 제외)
     - 객체 close() (ResultSet, Statement, Connection)

    여기서 실제로 개발자의 비지니스 로직이 들어가는 부분은 4번과 5번의 일부입니다. 나머지는 음... 기계적인 코드입니다. 실제 코드를 예로 들면 아래와 같습니다.

    Connection.java

    .........
    Connection con = null;
    Statement st = null;
    ResultSet rs = null;
     
    try {
        con=DriverManager("jdbc:mysql://localhost/dbname","root","1234");  // dbname에는 사용하는 database 이름, root 계정, 패스워드는 1234

        Statement st=con.createStatement();
        ResultSet rs=st1.executeQuery("select * form names");
     
        if( rs.next() ) {
            do {
                // result set을 잘 정리합니다. 물론 일일이
                ...
                // 비지니스 로직을 수행합니다.
                ...
            } while( rs.next() )
        }
    } catch (Exception ex) {
        // 적절한 예외 처리
    } finally {
        if( rs != null) rs.close();
        if( st != null ) st.close();
        if( con != null ) conn.close();
    }
    .........

    의외로 너무 많은 부분인 반복되는 느낌입니다. 이런 부분은 Spring을 사용하면 많이 줄어들게 됩니다. 다음절에서 확인해 보지요.

    2. DaoSupport in Spring Framework
    Spring에는 Template이라는 것을 이용해서 이 과정을 알아서 처리해 줍니다.
     - JdbcTemplate
     - NamedParameterJdbcTemplate
     - SimpleJdbcTemplate
     
    이를 편하게 사용하기 위해서 주로 아래의 Dao 클래스를 이용합니다.
     - JdbcDaoSupport
     - NamedParameterJdbcDaoSupport
     - SimpleJdbcDaoSupport

    여기서 가장 다양한 기능을 가진 SimpleJdbcDaoSupport 클래스의 사용법을 코드에서 확인해 보도록 하겠습니다. (전체 코드는 이전 4번 튜토리얼 분석 포스트에서 사용했던 예제를 보시면 됩니다.)

    applicationContext.xml

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
     
    <bean id="productDao" class="springapp.repository.JdbcProductDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    ProductDao.java

    public class JdbcProductDao extends SimpleJdbcDaoSupport implements ProductDao {
        public int selectCount() {
            return getSimpleJdbcTemplate().queryForInt(
                    "select count(*) from products");
        }
    }

    음..확실히 간편해 진것 같습니다.

    하지만, 쿼리문이 코드 내부에 하드코딩되어 있다는 점이 약간 아쉬운 부분입니다. 쿼리문 변경 시에 코드도 같이 변경되고 재컴파일도 되어야 합니다. (뭐 솔직히 컴파일 하면 되긴 해요... ^^) 그리고 따로 설명하지 않겠지만 ParameterizeRowMapper 인터페이스를 이용해서 domain 객체와 결과를 연결하는 방법이 있는데 이 역시 하드코딩 되기 때문에 유지 보수의 유연성이 떨어지는 면이 없지 않습니다.

    그래서, 쿼리 및 쿼리의 결과와 객체간의 연결을 외부에서 따로 관리하는 기능을 요구하게 되고 이런 기능을 가진 iBatis가 최근 많이 사용되고 있습니다. 다음 절에서 iBatis의 연동에 대해 알아보겠습니다.
     
    3. Spring Framework와 iBatis 연동
    1) iBatis
    국내 SI 현실에 가장 잘 어울린다고 평가받고 있는 persistance framework 입니다. 그 이유는 SQL 쿼리문을 그대로 사용하기 때문에 기존 JDBC 개발자들에게 거부감이 덜하고  경험적(?)인 시스템의 이해가 빠르기 때문일 겁니다. 여타의 ORM framwork는 배우기도 힘들고 또는 제 3자 입장에서 이해하기도 힘들어서 우리나라 SI 업계처럼 개발팀이 이합집산하는 문화에는 어울리지 않는다는 군요.

    각설하고 간단한 예를 보도록 하지요. iBatis에 대해서는 제가 따로 설명하지 않을 것입니다. 아래의 자료들을 보시면 간단하게 이해하실 수 있을 겁니다. 그리고, googling 하시면 JPetStore라는 예제가 있습니다. 이를 한번 실행시켜 보시는 것도 도움이 될 것입니다.

    다음은 iBatis 관련해서 많은 자료를 만드신 이동국님의 Tutorial 변역본입니다. 8장밖에 안되지만 iBatis의 동작에 대해서 이해하는데 많은 도움이 되실 것입니다.
    별첨으로 하나 더 첨부합니다. 이 파일은 sqlMap의 사용법에 대해 설명된 문서이고 역시 이동국님이 번역하신 자료입니다.

    자~! 이제 iBatis의 사용법에 대해서 간단히 감을 잡으셨나요?
    앞 절에서 언급한 것처럼 제 생각에는 2가지의 큰 장점을 가진다고 생각됩니다. (다른 장점이 있으시면 사알짝 좀 알려주세요~~)
     - SQL 쿼리문이 외부 설정 파일에서 관리된다.
     - domain 객체와 SQL result set의 연결이 외부 설정 파일에서 관리된다.

    그런데 JDBC와 마찬가지로 iBatis에도 단순하게 기계적으로 반복되는 코드가 있습니다. 바로 SqlMapClient를 선언하는 부분이지요! (아래 코드 참조)
    public MyAppSqlConfig {
        private static final SqlMapClient sqlMap;
        static {
            try {
                String resource = “com/ibatis/example/sqlMap-config.xml”;
                Reader reader = Resources.getResourceAsReader (resource);
                sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
            } catch (Exception e) {
                .......
        }
    }
     
    JDBC를 지원하기 위해 JdbcDaoSupport 클래스가 있지요? 그럼 SqlMapClient를 지원해 주는 것은? 네! SqlMapClientDaoSupport 클래스 입니다. 그럼 드디어 sqlMapClientDaoSupport 클래스에 대해 알아보겠습니다.

    2) sqlMapClientDaoSupport
    앞서 알아본 JdbcDaoSupport와 이름도 비슷하지만 사용법도 매우 유사합니다. 설정 파일과 관련된 자바 코드를 보시지요.
    applicationContext.xml - sqlMapConfig.xml - Product.xml 이 연쇄적인 관계를 가지고 있습니다. 여기서 applicationContext.xml은 spring 설정 파일이고 나머지는 iBatis 설정 파일입니다.
    비지니스 로직 수행 중 iBatisMessageDao의 함수들이 요청될 것이고 이떄 Product.xml에 있는 쿼리문들을 호출해서 수행하게 됩니다.
    주석문을 달고 색깔로 구분해 놓았습니다. 연관 관계에 유의하시면서 주의 깊게 확인하시길 바랍니다. (오랜만에 칼라풀한 포스트 입니다. ^^)

    applicationContext.xml

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
    </bean>

    // sqlMapClient를 선언해야 합니다.
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
         <property name="dataSource" ref="dataSource" />
         <property name="configLocation" value="WEB-INF/sqlmap/sqlMapConfig.xml" />
    </bean>

    // DAO 입니다.
    <bean id="productDao" class="springapp.repository.iBatisProductDao">
         <property name="sqlMapClient" ref="sqlMapClient" />
    </bean>

    <bean id="productManager" class="springapp.service.SimpleProductManager">
            <property name="productDao" ref="productDao" />
    </bean>

    sqlMapConfig.xml

    // classpath가 root 디렉토리 임에 유의 하셔야 합니다.
    <sqlMapConfig>
        <sqlMap resource="../sqlmap/Product.xml" />
    </sqlMapConfig>

    Product.xml

    <sqlMap namespace="ProductNamespace">
        // Product 도메인 객체 입니다. 여기저기서 사용되게 됩니다.
        <typeAlias alias="Product" type="springapp.domain.Product" />

        <!-- getProductList -->
        // getProductList와 관련된 결과 값의 형태를 정의합니다.
        <resultMap id="getProductResultMap" class="Product">
            <result property="id" column="id" />
            <result property="description" column="description" />
            <result property="price" column="price" />
        </resultMap>
     
        <select id="getProductList" resultMap="getProductResultMap" resultClass="Product">
            select id, description, price from products
        </select>

        <!-- saveProduct -->
        // saveProduct와 관련된 입력 값의 형태를 정의합니다.
        <parameterMap id="saveProductParamMap" class="Product">
            <parameter property="id"  />
            <parameter property="description"  />
            <parameter property="price"  />
        </parameterMap>
     
        // parameter를 지정하는 방식에 유의 하시길 바랍니다.
        <update id="saveProduct" parameterClass="Product" >
            update products set description = (#description#), price = (#price#) where id = (#id#)
        </update>
    </sqlMap>

    iBatisMessageDao.java
     
    // 코드 내부에는 전혀 쿼리 문이 없습니다!! 여기에 별 5개 꼭 확인하세요!
    public class iBatisProductDao extends SqlMapClientDaoSupport implements ProductDao {
        @SuppressWarnings("unchecked")
        public List<Product> getProductList() {
            List<Product> products = getSqlMapClientTemplate().queryForList("getProductList");
            return products;
        }

        public void saveProduct(Product prod) {
            getSqlMapClientTemplate().update("saveProduct", prod);
        }
    }

    2절에서 설명한 JdbcDaoSupport 클래스의 사용법과 비교했을 때 iBatis는 이를 한단계 더 추상화 되어 있음을 알 수 있습니다. 모든 쿼리문과 해당 쿼리 결과와 객체간의 관계를 XML 외부 설정으로 따로 관리하고 있다는 것이죠.
    그렇기 때문에, 앞에서 말씀드린데로 이 방법은 java 코드내에 하드코딩하는 것보다 개발의 유연성을 높이고 추후 관리시에 유리한 장점을 가집니다. (추상화란 처음에는 귀찮고 나중에는 편리한 것이지요!)

    다음은 이전 4번째 spring 프레임워크 포스트에서 사용했던 튜토리얼을 iBatis로 연동시킨 예제입니다. 위에 설명드린 내용이 어떻게 적용되어 있는지 한번 더 확인하실 수 있을 것으로 생각됩니다.


    4. 맺음말
    휴..정말 긴 튜토리얼 리뷰였습니다. 매일 생각없이 JDBC로 개발하던 저에게는 참으로 재미난 것이 아닐 수 없었습니다.
    물론 때에 따라서는 JDBC 자체가 빛나는 순간이 있겠지만, 대부분의 경우에는 iBatis가 우리를 편하게 해주지 않을까 생각됩니다. iBatis 관련 공부 중 이런 글이 있었습니다.
    "iBatis는 80%의 JDBC 기능을 20%의 자바 코드로 구현한 간단한 프레임워크이다."
    맞습니다. 이는 만병통치약이 아닙니다. 다만 대부분에 경우에 잘 먹히는 고마운 녀석인 것이지요. 뭐 종합감기약 정도 ^^;
    그렇기 때문에 여러분도 한번 직접 경험해 보시고 개인적인 판단을 세우시길 바랍니다. 여러분에게 맞지 않을 수 있으니까요. 이상입니다. 감사합니다.

    5. 참고자료
    1) 웹 개발자를 위한 스프링 2.5 프로그래밍 (가메출판사, 최범균 지음)





    출처 : http://www.iamcorean.net/
    Posted by 1010