연결 시 '로컬 리소스' 탭을 누른다. 원격지 PC에서 내 PC로 데이터를 복사하려면 '로컬 서비스' 항목에서 '디스크 드라이브'에 반드시 체크 표시를 해야 한다.

'프로그래밍 > Library' 카테고리의 다른 글

[EXCEL] 셀 범위로 드롭다운 목록 만들기  (0) 2009.01.29
[log4j] 사용례  (0) 2009.01.08
[펌] JExcelApi & POI  (0) 2007.11.28
[펌] 자바로 엑셀 때려잡기!!  (0) 2007.11.28
[펌] 각종 제어  (0) 2007.11.28

[펌] JExcelApi & POI

프로그래밍/Library 2007. 11. 28. 11:06 Posted by galad

JExcelApi(jxl.jar 다운로드)


http://www.andykhan.com/jexcelapi/index.html 에서 download JExcelApi에 가면
(http://www.andykhan.com/jexcelapi/download.html) tar.gz로 되어 있는데 압축풀면 jxl.jar있음


POI다운로드
http://jakarta.apache.org/poi/index.html /download/release


[엑셀파일읽기]

<%@ page language="java" %>
<%@ page contentType="text/html; charset=euc-kr"%>
<%@ page import="java.io.File,jxl.*"%>
<%


Workbook workbook = Workbook.getWorkbook(new File("E:/_Workspace/blr/blr/cms/excel/data/Book1.xls"));


Sheet sheet = workbook.getSheet(0);
Cell a1 = sheet.getCell(0,0);
Cell b2 = sheet.getCell(1,1);
Cell c3 = sheet.getCell(0,2);


String stringa1 = a1.getContents();
String stringb2 = b2.getContents();
String stringc3 = c3.getContents();


workbook.close();


%>


<%=stringa1%>
<%=stringb2%>
<%=stringc3%>


[엑셀파일로 저장]


<%@ page language="java" %>
<%@ page contentType="text/html; charset=euc-kr"%>
<%@ page import="java.io.File,java.util.Date,jxl.*,jxl.write.*"%>
<%
WritableWorkbook workbook = Workbook.createWorkbook(new File("E:/_Workspace/blr/blr/cms/excel/data/aaa.xls"));


WritableSheet sheet = workbook.createSheet("First Sheet", 0);
Label label = new Label(0,2,"A label record");
sheet.addCell(label);


// java.lang.Number 인지 jxl.write.Number  구분해야지^^


jxl.write.Number number = new jxl.write.Number(3,4,3.1415);
sheet.addCell(number);


workbook.write();
workbook.close();
%>


[POI로 테이블에 값넣기] poi.jsp참조


/***************************************************************************************/


엑셀파일처리 POI,JXL비교해 보았습니다.


10000번 for문을 돌려 첫번째 칼럼에 데이타를 입력하였습니다.


이걸 10번 반복한 체크시간입니다.




측정시간단위(밀리세컨)
D:\>java writeTest
============= jxl write time =============
0=>write time::719
1=>write time::281
2=>write time::250
3=>write time::266
4=>write time::234
5=>write time::266
6=>write time::234
7=>write time::266
8=>write time::234
9=>write time::329
=============      end      =============
============= poi write time =============
0=>write time::1110
1=>write time::922
2=>write time::1031
3=>write time::734
4=>write time::1328
5=>write time::766
6=>write time::797
7=>write time::1515
8=>write time::1719
9=>write time::2890
=============      end      =============



거의 2~3배 가까이 시간의 차이가 있더군여.. 단순히 엑셀파일만 다룬다면
jxl패키지를 사용하시는게 훨씬 좋을듯 합니다.

참조 : www.javapattern.info         www.okjsp.pe.kr


 자바 개발자라면 한번쯤은 Anti-MS를 외치면서,
무조건적으로 빌모씨 욕한번쯤은 해보았을 것이다.


허나 실상, 개발은 MS의 운영체제위에서 하고 있고,
이 창문이란게 없으면 넘 불편할 것도 같다.


특히, 고객은 모든 데이터를 Excel상에서 보고싶어 한다.


이런이유로 자바 개발자라고 해도, 이 엑셀을 잘 활용하여,
고객만족의 기쁨을 누려야 할 것이다.




자바로 엑셀을 핸들링 할 수 있는 방법은 크게 두가지로 나누어 진다.
1. Java Excel API   
    참조 : http://www.andykhan.com/jexcelapi/
2. POI
    참조 : http://jakarta.apache.org/poi/index.html




    흔히 POI를 엑셀을 핸들링 하기 위한 것으로만 오해하기 쉬운데,
    POI 프로젝트는 마이크로소프트 OLE 2 복합도큐먼트포맷형식의 파일을 순수 자바를 이용하여 핸들링하는 APIs로 구성되어있다.
    OLE 2 복합도큐먼트포맷형식의 파일은 마이크로소프트 엑셀 혹은 워드파일 등의 대부분의 오피스파일들을 나타낸다.




일반적으로 엑셀에 대한 핸들링만을 수행할 때에는 Jxl을 권장한다.
엑셀을 핸들링 할 때 엑셀에서 가장 작은 단위는 알고 있듯이 "셀"이다.
모든 작업은 이 셀을 중심으로 이루어진다.




주의할 점)
1) 엑셀 쉬트상에 "C15"라는 셀에 데이터가 있다고 가정하자.( 15행 C열을 나타낸다.)
이 때 Jxl에서는  A1( 1행 A열)부터  C15까지는 실제 데이터가 없을 경우에라도 null이 아닌 빈데이터가 있다고 인식한다.
즉 D열 이상이나, 16행 이상을 접근 할 때에 null로 인식한다.


하지만 POI에서는 C15 이내에 있다 하더라도 실제 데이터가 없을 때에는 null로 인식한다.


2) Jxl에서는 각 셀의 데이터 타입을 실제 엑셀 파일에서 지정된 셀의 타입을 따르고,
   POI에서는 셀의 실제 데이터 형을 따른다.
   예를 들어 특정 셀의 타입을 text 로 잡아놓고, 데이터를 1234로 입력하면
   Jxl에서는 "12345"로 인식하고, POI에서는 12345.00 이런식으로 인식한다.




EX ) Java Excel API를 이용한 Excel 읽기


import jxl.*;


// .. 중간생략


    Workbook workbook = null;
    Sheet sheet = null;
    Cell cell = null;


    try
    {
        //엑셀파일을 인식
        workbook = Workbook.getWorkbook( new File( szFileName));


        //엑셀파일에 포함된 sheet의 배열을 리턴한다.
        //workbook.getSheets();


        if( workbook != null)
        {
            //엑셀파일에서 첫번째 Sheet를 인식
            sheet = workbook.getSheet(0);


            if( sheet != null)
            {
                //셀인식 Cell a1 = sheet.getCell( 컬럼 Index, 열 Index);
                //셀 내용 String stringa1 = a1.getContents();


                //기록물철의 경우 실제 데이터가 시작되는 Row지정
                int nRowStartIndex = 5;
                //기록물철의 경우 실제 데이터가 끝 Row지정
                int nRowEndIndex   = sheet.getColumn( 2).length - 1;


                //기록물철의 경우 실제 데이터가 시작되는 Column지정
                int nColumnStartIndex = 2;
                //기록물철의 경우 실제 데이터가 끝나는 Column지정
                int nColumnEndIndex = sheet.getRow( 2).length - 1;


                String szValue = "";


                for( int nRow = nRowStartIndex; nRow <= nRowEndIndex; nRow++ )
                {
                    for( int nColumn = nColumnStartIndex; nColumn <= nColumnEndIndex ; nColumn++)
                    {
                        szValue = sheet.getCell( nColumn, nRow).getContents();


                        System.out.print( szValue);
                        System.out.print( "\t" );
                    }
                    System.out.println();
                }
            }
            else
            {
                System.out.println( "Sheet is null!!" );
            }
        }
        else
        {
            System.out.println( "WorkBook is null!!" );
        }
    }
    catch( Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        if( workbook != null)
        {
            workbook.close();
        }
    }


EX ) POI를 이용한 Excel 파일 읽기


import org.apache.poi.hssf.usermodel.*;


// .. 중간 생략


    HSSFWorkbook workbook = null;
    HSSFSheet sheet = null;
    HSSFRow row = null;
    HSSFCell cell = null;


    try
    {
        workbook = new HSSFWorkbook( new FileInputStream( new File( szFileName)));


        if( workbook != null)
        {
            sheet = workbook.getSheetAt( 0);


            if( sheet != null)
            {


                //기록물철의 경우 실제 데이터가 시작되는 Row지정
                int nRowStartIndex = 5;
                //기록물철의 경우 실제 데이터가 끝 Row지정
                int nRowEndIndex   = sheet.getLastRowNum();


                //기록물철의 경우 실제 데이터가 시작되는 Column지정
                int nColumnStartIndex = 2;
                //기록물철의 경우 실제 데이터가 끝나는 Column지정
                int nColumnEndIndex = sheet.getRow( 2).getLastCellNum();


                String szValue = "";


                for( int i = nRowStartIndex; i <= nRowEndIndex ; i++)
                {
                    row  = sheet.getRow( i);


                    for( int nColumn = nColumnStartIndex; nColumn <= nColumnEndIndex ; nColumn++)
                    {
                        cell = row.getCell(( short ) nColumn);
                       
                        if( cell == null)
                        {
                            continue;
                        }
                        if( cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC)
                        {
                            szValue = String.valueOf( cell.getNumericCellValue());
                        }
                        else
                        {
                            szValue = cell.getStringCellValue();
                        }


                        System.out.print( szValue);
                        System.out.print( "\t" );
                    }
                    System.out.println();
                }
            }
            else
            {
                System.out.println( "Sheet is null!!" );
            }
        }
        else
        {
            System.out.println( "WorkBook is null!!" );
        }


    }catch(Exception e){
        e.printStackTrace();
    }



EX ) Jxl을 이용하여 Excel에 데이터 저장하기


import jxl.*;


// .. 중간생략


    WritableWorkbook workbook = null;
 WritableSheet sheet = null;


 File excelFile = new File( szFileName);
 Label label = null;


 long start = 0;
   long end = 0;
   
    try
    {
        for(int i = 0 ; i < 10; i++)
        {
            workbook = Workbook.createWorkbook( excelFile);
            workbook.createSheet("sheet1", 0);
            sheet = workbook.getSheet(0);
            for( int j = 0; j < 10000; j++){
                label = new Label( j, 0, "test cell");
                sheet.addCell( label);
            }


            kidsbook.write();
            kidsbook.close();
        }
    }
    catch( Exception e)
    {
    }


// .. 중간생략



EX ) POI를 이용한 브라우저에서 Excel에 데이터 저장하여 보여주기


import org.apache.poi.hssf.usermodel.*;


// ... 생략


public void writeStream( PTSEvaluation[] arrPTSEvaluation) throws Exception
{
    try
    {


        HSSFWorkbook wb = new HSSFWorkbook();
        HSSFSheet sheet = wb.createSheet( "new sheet");
        HSSFRow row = null;
        HSSFCell cell = null;
        HSSFCellStyle style = null;


        ServletOutputStream excelOut = ServiceContext.getResponse().getOutputStream();
        ServiceContext.getResponse().setHeader( "Content-Disposition", "attachment;filename=EvaluationCompensationList.xls");
        ServiceContext.getResponse().setContentType( MimeType.getMimeType( "xls"));


        //로우 생성
        row = sheet.createRow( ( short)0);
        row.setHeightInPoints( 30);


        //셀에 적용한 스타일을 생성한다.
        style = PTSUtil.setExcelHeaderStyle( wb);


        // 셀 생성
        cell = row.createCell( (short)0);


        //한글 처리
        cell.setEncoding( HSSFCell.ENCODING_UTF_16); 


        //셀에 데이터 입력하기
        cell.setCellValue( "값");


        //셀에 스타일 적용하기
        cell.setCellStyle(style);


        //.. 중간생략 ( 이런 방식으로 로우와 셀을 증가 시키면서 작업을 수행하면 된다.


        wb.write( excelOut);
        excelOut.flush();
    }
    catch( Exception e)
    {
        e.printStackTrace();
        throw e;
    }
    finally
    {
        ServiceContext.getResponse().getOutputStream().close();
    }
}


// ... 생략

[펌] 각종 제어

프로그래밍/Library 2007. 11. 28. 11:04 Posted by galad

http://blog.naver.com/galad/140037028567

출처 블로그 > 맛깔나는 인생
원본 http://blog.naver.com/dltjdals8110/50003851272

 /**
  * コード類をフォーマット
  *
  * 与えられたサイズに足りない場合は空白を入れてフォーマットする
  * @param cd
  * @param size
  * @return
  */
 public static String cdFormat(String cd, int size) {
  String formatStr;
 
  if(cd == null || Constant.EMPTY_STR.equals(cd)){
   formatStr = cd;
  } else {
   for(int i = 0; i < size; i++){
    cd = cd + Constant.SPACE;
   }
   
   formatStr = cd.substring(0, size);
  }
 
  return formatStr;
 }
 
 /**
  *  文字列の後ろの半角スペースを削除する
  * 문자열의 뒤의 반각 스페이스를 삭제한다
  * @param str:文字列
  * @return 後ろの半角スペースを削除した文字列
  */
 public static String rTrim(String str) throws Exception{

  if(str != null && !Constant.EMPTY_STR.equals(str)){

   if(Constant.EMPTY_STR.equals(str.trim())){
    str = str.trim();
   } else {
    int len = str.length()-1;
   
    int i;
    for(i=0;;i++){
     if(str.charAt(len-i) != ' ') break;
    }
    str = str.substring(0,len-i+1);
   }
  }
 
 
  return str;
 }
 
 /**
  * nullチェック
  * @param str
  * @return bookean true:可 false:null
  * @throws Exception
  */
 public static boolean chkNull(String str) throws Exception{
 
  if(str == null || str.trim().equals(Constant.EMPTY_STR)){
   return false;
  }
 
  return true;
 }
 
 /**
  * 半角英数字チェック

  *반각영숫자 체크
  * @param myStr
  * @return boolean true:半角英数字あるいはnull false:半角英数字以外
  */
 public static boolean chkHanAlNum(String myStr) throws Exception{

     if(!chkNull(myStr)){
         return true;
     }
     myStr = myStr.trim();
  for (int i = 0; i < myStr.length(); i++) {

   char charData = myStr.charAt(i);
   if (((charData < 'A') || (charData > 'Z'))
     && ((charData < 'a') || (charData > 'z'))
     && ((charData < '0') || (charData > '9'))) {
    return false;
   }
  }
  return true;
 }
 
 
 /**
  * 半角チェック
  * @param str
  * @return boolean true:半角 false:全角
  * @throws Exception
  */
 public static boolean chkHankaku(String str) throws Exception{
    
     // nullだったら正常修了
     if(!chkNull(str)){
         return true;
     }
    
     byte[] bytes = str.getBytes();
        int beams = str.length();
        if (beams == bytes.length) {
            return true;
        } else {
            return false;
        }
 }
 
 /**
  * 半角カナを含ませない半角チェック

  *반각 가나를 포함하게하지 않는 반각 체크
  * @param str
  * @return boolean true:半角 false:全角あり、半角カナあり
  * @throws Exception
  */
 public static boolean chkHankakuNotHankana(String str) throws Exception{
    
     // nullだったら正常修了
     if(!chkNull(str)){
         return true;
     }
    
     // 半角カナが含まれていたら不可
     if(!chkHanKana(str)){
         return false;
     }
    
     // 半角チェック
     if(!chkHankaku(str)){
         return false;
     }
    
     return true;
 }
 
 
 
 /**
  * 入力数値のサイズチェック
  * 입력 수치의 사이즈 체크
  * 整数部と小数部のサイズが指定したとおりであるかチェックする
  * @param numInString
  * @param leftSize
  * @param rightSize
  * @return
  * @throws Exception
  */
 public static boolean chkNumSize(String numInString, int leftSize, int rightSize) throws Exception {
    
     if(numInString == null || Constant.EMPTY_STR.equals(numInString)){
         return true;
     }
    
     //数値解釈可能か?

    //수치 해석 가능한가
     try{
         Double.parseDouble(numInString);
     }catch(NumberFormatException e){
         return false;
     }
    
     //整数部、小数部の桁数は指定通りか?

    //정수부, 소수부의 자리수는 지정 대로인가?
     if(numInString.charAt(0) == '-'){
         numInString = numInString.substring(1);
     }
     String[] splited = numInString.split("\\.");
     if(splited[0].length() > leftSize){ //整数部
         return false;
     }
     // 小数部がなければ終了

    //소수부가 없으면 종료
     if(splited.length <= 1){
         return true;
     }
     if(splited[1].length() > rightSize){ //小数部
         return false;
     }
    
     return true;
 }

 /**
  * 半角数字チェック

  *반각 숫자 체크
  * @param strInString
  * @return 半角数字でなければfalseを返す
  */
    public static boolean chkNum(String strInString) throws Exception{
    
        if(!chkNull(strInString)){
         return true;
     }
        strInString = strInString.trim();
        //文字列の長さ分繰り返し
        int intChk;
        intChk = 0;
        for (int i = 0; i < strInString.length(); i++) {
            char c = strInString.charAt(i);

            if ((c < '0' && c != '.') || (c > '9' && c != '.') ) {
                return false;
            }
        }
       
       
        if (intChk == 0) {
            return true;
        } else {
            return false;
        }
    }
   
    /**
     * <PRE>
     * 半角数字(小数を含む)チェック

    *반각 숫자(소수를 포함한다) 체크
     * 空白文字を許容しない
     * @param decStr:半角数字(小数を含む)
     * @return 半角数字(小数を含む):true 半角数字以外:false
     * @throws Exception
     * </PRE>
     */
    public static boolean chkDecimal(String decStr) throws Exception{
 
        if(!chkNull(decStr)){
         return false;
     }
        for (int i = 0; i < decStr.length(); i++) {
            char c = decStr.charAt(i);

            if ((c < '0' && c != '.') || (c > '9' && c != '.') ) {
                return false;
            }
        }
       
        return true;

    }

 /**
  * <PRE>
  * 半角正の整数チェック

  * 반각정의 정수 체크
  * 空白文字を許容しない

  * 공백 문자를 허용 하지 않는다
  * @param intStr:半角正の整数
  * @return true:半角正の整数 false:半角正の整数
     * @throws Exception
     * </PRE>
  */
    public static boolean chkPositiveInt(String intStr) throws Exception{
    
        if(!chkNull(intStr)){
         return false;
     }
        for (int i = 0; i < intStr.length(); i++) {
            char c = intStr.charAt(i);

            if ((c < '0' ) || (c > '9') ) {
                return false;
            }
        }
       
        return true;
    }

    /**
  * 半角正の整数チェック

  *반각정의 정수 체크
  * @param strInString
  * @return 半角正の整数でなければfalseを返す
  */
    public static boolean chkPositiveInteger (String strInString) throws Exception{
    
        if(!chkNull(strInString)){
         return true;
     }
        strInString = strInString.trim();
        //文字列の長さ分繰り返し
        int intChk;
        intChk = 0;
        for (int i = 0; i < strInString.length(); i++) {
            char c = strInString.charAt(i);

            if ((c < '0' ) || (c > '9') ) {
                return false;
            }
        }
       
        if (intChk == 0) {
            return true;
        } else {
            return false;
        }
    }
   
    /**
     * 文字列からカンマ削除

     *문자열로부터 콤마 삭제
     * @param strNum
     * @return
     */
    public static String deleteComma(String strNum) throws Exception{

        //トークン格納用  토큰 격납용
        String strNextToken = Constant.EMPTY_STR;

        //戻り値格納用(編集後数値)  반환값 격납용(편집 후 수치)
        String strNewNum = Constant.EMPTY_STR;

        StringTokenizer tokenizer = new StringTokenizer(strNum, Constant.COMMA);

        //トークンが存在する間ループし変数にトークンを格納

       //토큰이 존재하는 동안 루프 해 변수에 토큰을 격납
        while (tokenizer.hasMoreTokens()) {
            strNextToken = tokenizer.nextToken();
            strNewNum += strNextToken;
        }

        return strNewNum;

    }

    /**
     * カンマをつける

     *콤마를 붙인다
     * @param strNum
     * @return
     */
    public static String numFormat(String strNum) throws Exception{

        //戻り値格納用(編集後数値)  반환값 격납용(편집 후 수치)
        String strNewNum = Constant.EMPTY_STR;

        //マイナスフラグ(-(マイナス)記号存在有無)

       //마이너스 플래그(-(마이너스) 기호 존재 유무)
        boolean blnMinus = false;

        // 文字列がnullだったら修了  문자열이 null라면 수료
        if (strNum == null || strNum.length() == 0) {
            strNewNum = null;
        } else {
            strNum = strNum.trim();
           
            if (strNum.substring(0, 1).equals(Constant.MINUS)) {
                strNum = strNum.substring(1, strNum.length());
                blnMinus = true;
            }

            //4桁目、7桁目、10桁目にカンマを挿入する

           //4자리수째, 7자리수째, 10 자리수째에 콤마를 삽입한다
            // int型なので10桁までを考えればよい

           //int형이므로 10 자리수까지를 생각하면 좋다
            for (int i = 0; i < strNum.length(); i++) {
                // ほかでは使用しないため例外的にマジックナンバー可

               //외에서는 사용하지 않기 때문에 예외적으로 magic number-가능
                if (((strNum.length() - i) == 4)
                        || ((strNum.length() - i) == 7)
                        || ((strNum.length() - i) == 10)) {
                    strNewNum += strNum.substring(i, i + 1) + Constant.COMMA;
                } else {
                    strNewNum += strNum.substring(i, i + 1);
                }
            }
        }

        //マイナス判定 마이너스 판정
        if (blnMinus == true) {
            return Constant.MINUS + strNewNum;
        } else {
            return strNewNum;
        }
    }
   
    /**
     * 文字列のバイト数チェック
     *
     * 指定したサイズより長ければfalse

     *지정한 사이즈보다 길면 false
     * @param str
     * @param byteSize バイト数
     * @return
     */
    public static boolean chkLength(String str, int byteSize) throws Exception{
       
        // 文字列がnullもしくは空文字の場合は正常終了

       //문자열이 null 혹은 공문자의 경우는 정상 종료
        if(str == null || Constant.EMPTY_STR.equals(str)){
            return true;
        }
       
        // 文字列のバイト数取得  문자열의 아르바이트수취득
        int len = str.getBytes("Windows-31J").length;
       
        // 文字列がサイズより長いかチェック  문자열이 사이즈보다 긴가 체크
        if(len > byteSize){
            return false;
        }
        return true;
    }
   
    /**
     * From,Toの相関チュック
     * From,To의 상관 체크
     * FromよりToのほうが大きな数値であればfalse

     *From보다 To 쪽이 큰 수치이면 false

     * @param from
     * @param to
     * @return
     */
    public static boolean chkFromTo(String from, String to) throws Exception{
       
        // 数値チェック  수치 체크
        if(!chkNum(from) || !chkNum(to)){
            // どちらかが数値でない  어느 쪽인지가 수치가 아니다
            return false;
        }
       
        // FROMとTOを数値に変換  FROM와 TO를 수치에 변환
        int nFrom = Integer.parseInt(from);
        int nTo = Integer.parseInt(to);
       
        // FromよりToが大きいのはfalse
        if(nFrom < nTo){
            return false;
        }
       
        return true;
    }

    /**
     * 半角カナチェック
     * 반각 가나 체크
     * 文字列に半角カナが含まれていればfalse

     *문자열에 반각 가나가 포함되어 있으면 false
     * @param str
     * @return
     */
    public static boolean chkHanKana(String str) throws Exception{
       
        if (str == null || str.length() == 0)
            return true;

        StringBuffer wkStr = new StringBuffer(str); // 変換バッファ
        for (int i = 0; i < wkStr.length(); i++) {

            // 半角カナ範囲(Unicode 0xFF61 ~ 0xFF9F)確認
            if (wkStr.charAt(i) >= 0xFF61 && wkStr.charAt(i) <= 0xFF9F) {
                return false;
            }
        }
        return true;
    }

   
    /**
     * コードテーブルからキーで該当するコードのリストを取得する

     *코드 테이블에서 키로 해당하는 코드의 리스트를 취득한다
     * @param key
     * @return
     * @throws Exception
     */
    public static List getCdBeans(String key) throws Exception {
        return _utilLogic.getCdBeans(key, false);
    }
   
    /**
     * コードテーブルからキーで該当するコードのリストを取得し、

     *코드 테이블에서 키로 해당하는 코드의 리스트를 취득해
     * その中から必要なコードのものだけのリストを取得する
     * 그 중에서 필요한 코드의 것만의 리스트를 취득한다
     * @param key
     * @param selectList
     * @return List
     * @throws Exception
     */
    public static List getCdBeansSelection(String key, List selectList) throws Exception {
       
        List resList = new ArrayList();
       
        // キーでコードのリスト取得   키로 코드의 리스트 취득
        List cdList = getCdBeans(key);
       
        for(int cdCnt = 0; cdCnt < cdList.size(); cdCnt++){
           
            CdBean bean = (CdBean)cdList.get(cdCnt);
           
            // リストの1番目(空白)は無条件に追加

           //리스트의 1번째 (공백)는 무조건 추가
            if(cdCnt == 0){
                resList.add(bean);
            }

            for(int selectCnt = 0; selectCnt < selectList.size(); selectCnt++){
               
                String selectCd = (String)selectList.get(selectCnt);
               
               
               
                // 選択したコードとテーブルから取得したコードが等しければリストに追加

               //선택한 코드와 테이블에서 취득한 코드가 동일하면 리스트에 추가
                if(bean.getValue().equals(selectCd)){
                   
                    resList.add(bean);
                }
               
            }
        }
       
        return resList;
    }
   
    /**
     * コードテーブルからキーで該当するコードのリストを取得する(not null 項目用)

     *코드 테이블에서 키로 해당하는 코드의 리스트를 취득한다(not null 항목용)
     * @param key
     * @return
     * @throws Exception
     */
    public static List getCdBeansNotNull(String key) throws Exception {
        return _utilLogic.getCdBeans(key, true);
    }
   
    /**
     * コードテーブルからキーで該当するコードのリストを取得し、

     *코드 테이블에서 키로 해당하는 코드의 리스트를 취득해,
     * その中から必要なコードのものだけのリストを取得する(not null 項目用)

     *그 중에서 필요한 코드의 것만의 리스트를 취득한다(not null 항목용)
     *
     * @param key
     * @param selectList
     * @return List
     * @throws Exception
     */
    public static List getCdBeansSelectionNotNull(String key, List selectList) throws Exception {
       
        List resList = new ArrayList();
       
        // キーでコードのリスト取得
        List cdList = getCdBeansNotNull(key);
       
        for(int cdCnt = 0; cdCnt < cdList.size(); cdCnt++){
           
            CdBean bean = (CdBean)cdList.get(cdCnt);

            for(int selectCnt = 0; selectCnt < selectList.size(); selectCnt++){
               
                String selectCd = (String)selectList.get(selectCnt);

                // 選択したコードとテーブルから取得したコードが等しければリストに追加

               //선택한 코드와 테이블에서 취득한 코드가 동일하면 리스트에 추가
                if(bean.getValue().equals(selectCd)){
                   
                    resList.add(bean);
                }
               
            }
        }
       
        return resList;
    }
   
    /**
     * 名称テーブルからキーで該当する名称のリストを取得する

     *명칭 테이블에서 키로 해당하는 명칭의 리스트를 취득한다
     * @param key
     * @return
     * @throws Exception
     */
    public static List getMeishoBeans(String key) throws Exception {
        return _utilLogic.getMeishoBeans(key, false);
    }
   
    /**
     * 名称テーブルからキーで該当する名称のリストを取得し、

     *명칭 테이블에서 키로 해당하는 명칭의 리스트를 취득해
     * その中から必要な名称のものだけのリストを取得する
     * 그 중에서 필요한 명칭의 것만의 리스트를 취득한다
     * @param key
     * @param selectList
     * @return List
     * @throws Exception
     */
    public static List getMeishoBeansSelection(String key, List selectList) throws Exception {
       
        List resList = new ArrayList();
       
        // キーで名称のリスト取得  키로 명칭의 리스트 취득
        List meiList = getMeishoBeans(key);
       
        for(int meiCnt = 0; meiCnt < meiList.size(); meiCnt++){
           
            CdBean bean = (CdBean)meiList.get(meiCnt);
           
            // リストの1番目(空白)は無条件に追加

           // 리스트의 1번째 (공백)는 무조건 추가
            if(meiCnt == 0){
                resList.add(bean);
            }

            for(int selectCnt = 0; selectCnt < selectList.size(); selectCnt++){
               
                String selectCd = (String)selectList.get(selectCnt);

                // 選択した名称とテーブルから取得した名称が等しければリストに追加

               //선택한 명칭과 테이블에서 취득한 명칭이 동일하면 리스트에 추가
                if(bean.getValue().equals(selectCd)){
                   
                    resList.add(bean);
                }
               
            }
        }
       
        return resList;
    }
   
    /**
     * 名称テーブルからキーで該当する名称のリストを取得する(not null 項目用)

     *명칭 테이블에서 키로 해당하는 명칭의 리스트를 취득한다(not null 항목용)
     * @param key
     * @return
     * @throws Exception
     */
    public static List getMeishoBeansNotNull(String key) throws Exception {
        return _utilLogic.getMeishoBeans(key, true);
    }
   
    /**
     * 名称テーブルからキーで該当する名称のリストを取得し、

     *명칭 테이블에서 키로 해당하는 명칭의 리스트를 취득해,
     * その中から必要な名称のものだけのリストを取得する(not null 項目用)
     * 그 중에서 필요한 명칭의 것만의 리스트를 취득한다(not null 항목용)
     * @param key
     * @param selectList
     * @return List
     * @throws Exception
     */
    public static List getMeishoBeansSelectionNotNull(String key, List selectList) throws Exception {
       
        List resList = new ArrayList();
       
        // キーで名称のリスト取得
        List meiList = getMeishoBeansNotNull(key);
       
        for(int meiCnt = 0; meiCnt < meiList.size(); meiCnt++){
           
            CdBean bean = (CdBean)meiList.get(meiCnt);
           

            for(int selectCnt = 0; selectCnt < selectList.size(); selectCnt++){
               
                String selectCd = (String)selectList.get(selectCnt);

                // 選択した名称とテーブルから取得した名称が等しければリストに追加

               //선택한 명칭과 테이블에서 취득한 명칭이 동일하면 리스트에 추가
                if(bean.getValue().equals(selectCd)){
                   
                    resList.add(bean);
                }
               
            }
        }
       
        return resList;
    }
   
    /**
     * SYSINFOテーブルから1レコード取得する

     *SYSINFO 테이블에서 1 레코드 취득한다
     * @return
     * @throws Exception
     */
    public static Sysinfo getSysinfo() throws Exception {
        return _utilLogic.getSysinfo();
    }

    /**
     * @param logic _utilLogic を設定。
     */
    public static void set_utilLogic(UtilLogic logic) {
        _utilLogic = logic;
    }
   


 /**
  * YYMMDDの文字列を受け取りCalendarを返す
  * YYMMDD의 문자열을 받아 Calendar를 돌려준다
  * @param myDate
  * @return
  * @throws Exception
  */
 private static Calendar getCalendar(String myDate) throws Exception{
    
        int intYear;
        int intMonth;
        int intDay;

        if (myDate.length() > 3) {
            intYear = java.lang.Integer.parseInt(myDate.substring(0, 4));
        } else {
            intYear = 0;
        }
        if (myDate.length() > 5) {
            intMonth = java.lang.Integer.parseInt(myDate.substring(4, 6));
        } else {
            intMonth = 0;
        }
        if (myDate.length() == 8) {
            intDay = java.lang.Integer.parseInt(myDate.substring(6, 8));
        } else {
            intDay = 0;
        }

        Calendar cal = new GregorianCalendar();
        cal.setLenient(false);
        cal.set(intYear, intMonth - 1, intDay);
       
        return cal;
 }
 
 
 /**
  * 日付妥当性チェック  일자 타당성 체크
  * @param myDate
  * @return
  */
 public static boolean chkDateFormat(String myDate) throws Exception{

     if(myDate == null || Constant.EMPTY_STR.equals(myDate)){
         return true;
     }
    
     // 文字列にスラッシュが入っていた場合消す

    // 문자열에 slash가 들어가 있었을 경우 지운다
     myDate = deleteSlash(myDate);
    
        //入力文字列が8桁以外の場合エラ-

       // 입력 문자열이 8자리수 이외의 경우 에러
     
        if (myDate.length() != 8) {
            return false;
        }

        for (int i = 0; i < myDate.length(); i++) {
            char charData = myDate.charAt(i);
            if ((charData < '0') || (charData > '9')) {
                return false;
            }
        }
 
        Calendar cal = new GregorianCalendar();
       
        cal = getCalendar(myDate);

        try {
            java.util.Date ud = cal.getTime();
        } catch (IllegalArgumentException iae) {
            return false;
        }

        return true;

    }
 
 /**
  * 日付の大小チェック
  * @param from
  * @param to
  * @return toがfromより大きければtrue そうでなければfalse
  * @throws Exception
  */
 public static boolean chkDateFromTo(String from, String to) throws Exception{
    
     // 文字列にスラッシュが入っていた場合消す

    //문자열에 slash가 들어가 있었을 경우 지운다
     from = deleteSlash(from);
     to = deleteSlash(to);
    
     // 日付のフォーマットが正しくなければ終了

    //일자의 포맷이 올바르지 않으면 종료
     if(!chkDateFormat(from) || !chkDateFormat(to)){
         return true;
     }
    
     // どちらかがnull なら終了
     if(from == null || Constant.EMPTY_STR.equals(from.trim())
             || to == null || Constant.EMPTY_STR.equals(to.trim())){
         return true;
     }
    
        Calendar calFrom = new GregorianCalendar();
        Calendar calTo = new GregorianCalendar();
       
        calFrom = getCalendar(from);
        calTo = getCalendar(to);
    
        // 比較
        return !calFrom.after(calTo);
 }
 
    /**
     * スラッシュをつける

     *slash를 붙인다
     * @param strNum
     * @return
     */
    public static String dateFormat(String strYmd) throws Exception{

        //戻り値格納用  반환값 격납용
        String strNewYmd = Constant.EMPTY_STR;

        // 文字列がnullだったら修了
        if (strYmd == null || strYmd.length() == 0) {
            strNewYmd = null;
        // 日付のフォーマットでなければnullを返して終了

       //일자의 포맷이 아니면 null를 돌려주어 종료
        } else if(!chkDateFormat(strYmd)){
            strNewYmd = null;
        } else {

            //3桁目、6桁目にスラッシュを挿入する
            strNewYmd =  strYmd.substring(0, 4) + Constant.SLASH + strYmd.substring(4, 6) +
                        Constant.SLASH + strYmd.substring(6, 8);

        }
        return strNewYmd;

    }

    /**
     * 文字列からスラッシュ削除  문자열로부터 slash 삭제
     * @param strNum
     * @return
     */
    public static String deleteSlash(String strDate) throws Exception{

        if(strDate == null){
            return null;
        }
       
        //トークン格納用  토큰 격납용
        String strNextToken = Constant.EMPTY_STR;

        //戻り値格納用  반환값 격납용
        String strNewNum = Constant.EMPTY_STR;

        StringTokenizer tokenizer = new StringTokenizer(strDate, Constant.SLASH);

        //トークンが存在する間ループし変数にトークンを格納

       //토큰이 존재하는 동안 루프 해 변수에 토큰을 격납
        while (tokenizer.hasMoreTokens()) {
            strNextToken = tokenizer.nextToken();
            strNewNum += strNextToken;
        }

        return strNewNum;

    }
   
    /**
     * 消費税を計算する

    *소비세를 계산한다
     * @param num
     * @return
     * @throws Exception
     */
    public static String taxCalc(String num) throws Exception{
       
        double tax;  // 消費税  소비세
        double amount; // 税込価格 세금 포함 가격
       
        if(num == null || Constant.EMPTY_STR.equals(num)){
            return null;
        }
       
        num = deleteComma(num);
       
        // 文字列が半角数字であるかチェック

       //문자렬이 반각 숫자일까 체크
        if(!chkNum(num)){
            return null;
        }

        // 消費税取得  소비세 취득
        Sysinfo sysinfo = _utilLogic.getSysinfo();
        tax = sysinfo.getTaxRate().doubleValue() / 100;
       
        amount = Integer.parseInt(num) * tax + Integer.parseInt(num);
       
        int i = (int)amount;
        return String.valueOf(i);
      
    }
   
   
   
    /**
     * 消費税計算をして表示用文言にフォーマットする

     *소비세 계산을 해 표시용 문언에 포맷 한다
     * @param num
     * @return
     * @throws Exception
     */
    public static String setTax(BigDecimal num) throws Exception{
     // 消費税計算  소비세 계산
        String taxStr = null;
       
        // 消費税計算とカンマづけを行う。소비세 계산과 콤마 지어를 실시한다.
        taxStr = Common.numFormat(Common.taxCalc(bigDecimalToStr(num)));
       
     // 結果nullでなければヘッダフッタを付加

    //결과 null가 아니면 헤더 footer를 부가
        if(taxStr != null){
            taxStr = Constant.TAX_HEADER + taxStr + Constant.TAX_FOOTER;
        }

     return taxStr;
       
    }

    /**
     * 消費税計算をして表示用文言にフォーマットする(引数String用)

     *소비세 계산을 해 표시용 문언에 포맷 한다(인수 String용)
     * @param num
     * @return
     * @throws Exception
     */
    public static String setTaxStr(String num) throws Exception{
     // 消費税計算
        String taxStr = null;
       
        // 消費税計算とカンマづけを行う。소비세 계산과 콤마 지어를 실시한다.
        taxStr = Common.numFormat(Common.taxCalc(num));
       
     // 結果nullでなければヘッダフッタを付加

     //결과 null가 아니면 헤더 footer를 부가
        if(chkNull(taxStr)){
            taxStr = Constant.TAX_HEADER + taxStr + Constant.TAX_FOOTER;
        } else {
            taxStr = Constant.EMPTY_STR;
        }

     return taxStr;
       
    }
   
    /**
     * BigDecimalをStringに変換する  (으)로 변환한다
     * @param num
     * @return
     */
    public static String bigDecimalToStr(BigDecimal num) throws Exception{
     
     if(num == null){
      return null;
     } else {
      return num.toString();
     }     
    }
   
 /**
  * 文字列型をBigdecimalに変換する 문자열형을 Bigdecimal로 변환한다
  * @param str
  * @return BigDecimal
  */
 public static BigDecimal strToBigDec(String str) throws Exception{
 
  if(str == null || Constant.EMPTY_STR.equals(str.trim()) ||
          !Common.chkNum(str)){
   return null;
  } else {
   
   return new BigDecimal(str);
  }
 
 }
 
 /**
  * StringをBigDecimal型に変換する
  * String를 BigDecimal형으로 변환한다
  * null, "", 数値以外の場合は0を返す
  * null, "", 수치 이외의 경우는 0을 돌려준다
  * @param str
  * @return
  * @throws Exception
  */
 public static BigDecimal strToBigDecNotNull(String str) throws Exception{
  if(str == null || Constant.EMPTY_STR.equals(str.trim()) ||
          !Common.chkNum(str)){
   return new BigDecimal(0);
  } else {
   
   return new BigDecimal(str);
  }
    
 }
 
 /**
  * 空文字だったらnullを返す  공문자라면 null를 돌려준다
  * @param String
  * @return String
  */
 public static String getEmptyOrStr(String str) throws Exception{
 
  if(str == null || Constant.EMPTY_STR.equals(str)){
   return null;
  } else{
   return str;
  }
 }
 
 /**
  * 空文字だったらnullを返す  공문자라면 null를 돌려준다
  * @param bigdecimal
  * @return String
  */
 public static BigDecimal getEmptyOrNumber(BigDecimal num) throws Exception{
 
  if(num == null || Constant.EMPTY_STR.equals(num)){
   return null;
  } else{
   return num;
  }
 }
 
 /**
  * 改行コードを<BR>に変換する

  *개행 코드를<BR>로 변환한다
  * @param str
  * @return
  */
 public static String replaceLF(String str){
    
     if(str == null || str.trim().equals(Constant.EMPTY_STR)){
         return null;
     }
    
     return str.replaceAll("\n", "<BR>");
 }
 
 /**
  * コード名を取得する
  * @param cdKey
  * @param cd
  * @return
  * @throws Exception
  */
 public static String getCdNm(String cdKey, String cd) throws Exception{
    
     CdTbl cdTbl = _utilLogic.getCdTbl(cdKey, cd);
    
     if(cdTbl == null){
         return null;
     } else {
         return cdTbl.getCdNm();
     }
 }
 
 /**
  * 名称名を取得する  명칭명을 취득한다
  * @param nmKbn
  * @param nmCd
  * @return
  * @throws Exception
  */
 public static String getMeishoNm(String nmKbn, String nmCd) throws Exception{
    
     Meisho meisho = _utilLogic.getMeisho(nmKbn, nmCd);
    
     if(meisho == null){
         return null;
     } else {
         return meisho.getName();
     }
 }
 
 /**
  * 全角チェック  전각 체크
  * 全角以外が入っていたらfalse 全部全角ならtrue

  *전각 이외가 들어가 있으면 false 전부 전각이라면 true
  * @param inStr
  * @return
  */
    public static boolean chkZenkaku(String inStr){
       
        if (inStr == null || inStr.length() == 0) {
            return true;
        }

        byte[] bytData;
        String strChar;
        boolean flgSingle = false;
        int intX;
        for (int i = 0; i < inStr.length(); i++) {
            try {
                strChar = inStr.substring(i, i + 1);
                bytData = strChar.getBytes("Windows-31J");
                for (int j = 0; j < bytData.length; j++) {
                    if (bytData[j] < 0) {
                        intX = 256 + bytData[j];

                    } else {
                        intX = bytData[j];
                    }
                }
                if (bytData.length == 2) {

                } else {
                    flgSingle = true;
                }
            } catch (Exception e) {
                flgSingle = false;
            }
        }

        if (flgSingle)
            return false;
        else
            return true;
    }

   
    /**
     * 検索上限チェック
     * 검색 상한 체크
     * システム限界数以上に設定していたらfalseを返す

     *시스템 한계수이상으로 설정해 있으면 false를 돌려준다
     * @param maxSize
     * @return true:システム上限内, null, 半角整数以外
     *    false:システム上限オーバー
     * @throws Exception
     */
    public static boolean chkMaxSearchNum(String maxSize) throws Exception{
      
       int maxNum;
       int sysMaxNum;
      
       if(!chkNull(maxSize)){
          
           return true;
       }
      
       // 半角数値チェック  반각 수치 체크
       if(!chkPositiveInteger(maxSize)){
          
           return true;
       }
      
       // システム的な最大件数  시스템적인 최대 건수
       sysMaxNum = Constant.SYS_MAS_SEARCH_NUMBER;
       // 入力された最大件数  입력된 최대 건수
       maxNum = Integer.parseInt(maxSize);
       // システム限界数より大きければfalse

      //시스템 한계수보다 크면 false
       if(maxNum > sysMaxNum){
           return false;
       }
       return true;
      
   }

    /**
     * 和暦チェック
     * 일본이 달력 체크
     * @param nengo 1:明治,2:大正,3:昭和,4:平成

     *1:메이지,2:타이쇼,3:쇼와,4:헤세이
     * @param date
     * @return
     * @throws Exception
     */
    public static boolean chkWaDate(String nengo, String date) throws Exception{
       
        boolean result = true;
       
        String nen;  // 年
        String tsuki;   // 月
        String niti;    // 日
        int intNen;
        int intTsuki;
        int intNiti;
        String seireki;  // 西暦
       
       
        // 桁数チェック(6バイトYYMMDD) 자리수 체크(6바이트 YYMMDD)
        if(date.length() != 6){
           
            return false;
        }
        // 数値チェック  수치 체크
        if(!chkPositiveInteger(date)){
           
            return false;
        }
        // 年、月、日を取得
        nen = date.substring(0, 2);
        tsuki = date.substring(2, 4);
        niti = date.substring(4, 6);
       
        // それぞれをintに変換 각각을 int에 변환
        intNen = Integer.parseInt(nen);
        intTsuki = Integer.parseInt(tsuki);
        intNiti = Integer.parseInt(niti);

        // 日付フォーマット通りの場合は各年号ごとに年月日をチェック

       //일자 포맷 그대로의 경우는 매년호 마다 연월일을 체크
        // 明治  메이지
        if(Constant.NENGO_MEIJI.equals(nengo)){
           
            // 45年を超える場合はありえない

           //45년을 넘는 경우는 있을 수 없다
            if(intNen > 45){
               
                return false;

            // 45年の場合は7月30日まで
            } else if(intNen == 45){
               
                if(intTsuki > 7){
                   
                    return false;
                } else if(intTsuki == 7){
                   
                    if(intNiti > 30){
                       
                        return false;
                    }
                   
                }
            // 1年の場合は9月8日から
            } else if(intNen == 1){
               
                if(intTsuki < 9){
                   
                    return false;
                } else if(intTsuki == 9){
                   
                    if(intNiti < 8){
                       
                        return false;
                    }
                }
            }
           
            // 西暦に変換
            seireki = String.valueOf(intNen + WAREKI_SA_MEIJI);
            seireki = seireki + tsuki + niti;
           
            if(!chkDateFormat(seireki)){
               
                return false;
            }
           
           
        // 大正
        } else if(Constant.NENGO_TAISHO.equals(nengo)){
           
            // 15年を超える場合はありえない
            if(intNen > 15){
               
                return false;
            // 15年の場合は12月25日まで
            } else if(intNen == 15){
               
                if(intTsuki > 12){
                   
                    return false;
                } else if(intTsuki == 12){
                   
                    if(intNiti > 25){
                        return false;
                    }
                }
            }
            // 1年の場合は7月30日から
            else if(intNen == 1){
               
                if(intTsuki < 7){
                    return false;
                } else if(intTsuki == 7){
                   
                    if(intNiti < 30){
                       
                        return false;
                    }
                }
            }
           
            // 西暦に変換
            seireki = String.valueOf(intNen + WAREKI_SA_TAISHO);
            seireki = seireki + tsuki + niti;
           
            if(!chkDateFormat(seireki)){
               
                return false;
            }
           
        // 昭和
        } else if(Constant.NENGO_SHOWA.equals(nengo)){
        
            // 64年の1月7日まで
            if(intNen > 67){
               
                return false;
            } else if(intNen == 64){
               
                if(intTsuki > 1){
                    return false;
                } else if(intTsuki == 1){
                   
                    if(intNiti > 7){
                        return false;
                    }
                }
            }
            // 1年は12月25日から
            if(intNen == 1){
               
                if(intTsuki < 12){
                   
                    return false;
                } else if(intTsuki == 12){
                   
                    if(intNiti < 25){
                        return false;
                    }
                }
            }
           
            // 西暦に変換
            seireki = String.valueOf(intNen + WAREKI_SA_SHOWA);
            seireki = seireki + tsuki + niti;
           
            if(!chkDateFormat(seireki)){
               
                return false;
            }
       
        // 平成
        } else if(Constant.NENGO_HEISEI.equals(nengo)){
           
            // 1年は1月8日から
            if(intNen == 1){
               
                if(intTsuki == 1){
                   
                    if(intNiti < 8){
                        return false;
                    }
                }
            }
           
            // 西暦に変換
            seireki = String.valueOf(intNen + WAREKI_SA_HEISEI);
            seireki = seireki + tsuki + niti;
           
            if(!chkDateFormat(seireki)){
               
                return false;
            }
        // それ以外の年号
        } else {
            return false;
        }
       
        // 現在日付よりも未来であればエラー
        if(!chkComparisonDate(seireki)){
            return false;
        }
       
       
        return result;
    }
   

   
    /**
     * 現在日付との比較
     * 현재 일자라는 비교
     * 現在日付よりも未来の日付であればfalseを返す

     *현재 일자보다 미래의 일자이면 false를 돌려준다
     * @param date
     * @return
     * @throws Exception
     */
    public static boolean chkComparisonDate(String date) throws Exception{
       
        String sysDateStr;
       
        // 日付妥当性チェック  일자 타당성 체크
        if(!chkDateFormat(date)){
           
            return false;
        }
       
        // 現在年月日取得
        Date sysDate = DateUtil.getSysdate();
     
        // SimpleDateFormatオブジェクトを生成

       //SimpleDateFormat 오브젝트를 생성
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
        sysDateStr = formatter.format(sysDate);
       
        // 日付の相関チェック呼び出し  일자의 상관 체크 호출
        if(!chkDateFromTo(date, sysDateStr)){
            return false;
        }
       
        return true;
    }

 /**
  * 日付妥当性チェック(YYYYMMの場合)

   *일자 타당성 체크(YYYYMM의 경우)
  * @param myDate
  * @return
  */
 public static boolean chkDateFormatYYYYMM(String myDate) throws Exception{

     String nen;
     String tuki;
     int intNen;
     int intTuki;
    
    
     if(myDate == null || Constant.EMPTY_STR.equals(myDate)){
         return true;
     }
    
     // 文字列にスラッシュが入っていた場合消す

     //문자열에 slash가 들어가 있었을 경우 지운다
     myDate = deleteSlash(myDate);
    
        //入力文字列が6桁以外の場合エラ-

       //입력 문자열이 6자리수 이외의 경우 에러
        if (myDate.length() != 6) {
            return false;
        }

        // 数字のみで構成されているかチェック

       //숫자만으로 구성되어 있을까 체크
        for (int i = 0; i < myDate.length(); i++) {
            char charData = myDate.charAt(i);
            if ((charData < '0') || (charData > '9')) {
                return false;
            }
        }
 
        // 年、月部分を取り出し  해, 달부분을 꺼내
        nen = myDate.substring(0,4);
        tuki = myDate.substring(4, 6);
       
        // 月部分をintに変換し、1から12の間であるかチェック

        //달부분을 int로 변환해, 1에서 12의 사이일까 체크
        intTuki = Integer.parseInt(tuki);
        if(intTuki < 1 || intTuki > 12){
           
            return false;
        }

        return true;

    }
 
 /**
  * 日付の大小チェック(YYYYMMの場合) 일자의 대소 체크(YYYYMM의 경우)
  * @param from
  * @param to
  * @return toがfromより大きければtrue そうでなければfalse 同じ場合はtrue
  * @throws Exception
  */
 public static boolean chkDateFromToYYYYMM(String from, String to) throws Exception{
    
     int intFrom;
     int intTo;
    
     // 文字列にスラッシュが入っていた場合消す

    //문자열에 slash가 들어가 있었을 경우 지운다
     from = deleteSlash(from);
     to = deleteSlash(to);
    
     // 日付のフォーマットが正しくなければ終了

    //일자의 포맷이 올바르지 않으면 종료
     if(!chkDateFormatYYYYMM(from) || !chkDateFormatYYYYMM(to)){
         return true;
     }
    
     // どちらかがnull なら終了
     if(!chkNull(from) || !chkNull(to)){
         return true;
     }
    
    
        // 年、月部分を取り出し  해, 달부분을 꺼내
     intFrom = Integer.parseInt(from);
     intTo = Integer.parseInt(to);
       
     // toよりfromのほうが先であればfalse

    //to보다 from 쪽이 앞이면 false
     if(intFrom > intTo){
         return false;
     }
       
     return true;
 }
 
    /**
     * スラッシュをつけるYYYYMM  slash를 붙이는 YYYYMM
     * @param strNum
     * @return
     */
    public static String dateFormatYYYYMM(String strYmd) throws Exception{

        //戻り値格納用  반환값 격납용
        String strNewYmd = Constant.EMPTY_STR;

        // 文字列がnullだったら終了
        if (strYmd == null || strYmd.length() == 0) {
            strNewYmd = null;
        // 日付のフォーマットでなければnullを返して終了

       //일자의 포맷이 아니면 null를 돌려주어 종료
        } else if(!chkDateFormatYYYYMM(strYmd)){
            strNewYmd = null;
        } else {

            //3桁目、6桁目にスラッシュを挿入する
            strNewYmd =  strYmd.substring(0, 4) + Constant.SLASH + strYmd.substring(4, 6);
//            //3桁目、6桁目にスラッシュを挿入する
//            strNewYmd =  strYmd.substring(0, 4) + Constant.SLASH + strYmd.substring(4, 6) +
//                        Constant.SLASH + strYmd.substring(6, 8);

        }
        return strNewYmd;

    }
   
   
    /**
     * application.propertiesのメッセージをキーにより取得する

     *application.properties의 메세지를 키에 의해 취득한다
     * @param key メッセージのキー
     * @return String メッセージ
     * @throws Exception
     */
    public static String getMessage(String key) throws Exception {
       
        ResourceBundle bundle = ResourceBundle.getBundle(Constant.MESSAGE_FILE_NAME);
        return bundle.getString(key);
    }
   
    /**
     * 文字列From,Toを比較する  문자열 From,To를 비교한다
     *
     * 辞書順で比較し、FromがToより大きければfalse

     *사전순서로 비교해, From가 To보다 크면 false
     * 同じ、もしくはFrom < To ならばtrue
     * @param from
     * @param to
     * @return
     * @throws Exception
     */
 public static boolean chkCdFromTo(String from, String to) throws Exception{
    
     // どちらかがnullだったらtrueで強制終了

    //어느 쪽인지가 null라면 true로 강제 종료
     if(!Common.chkNull(from) || !Common.chkNull(to)){
         return true;
     }
    
     // from > toだったらfalse
     if(0 < from.trim().compareTo(to.trim())) {
         return false;
     }
     return true;
    
 }
 
 
 
 /**
  * 整数チェック  정수 체크
  * @param iStr
  * @return true:正の整数,負の整数 false:整数以外

  *정의 정수, 부의 정수 false:정수 이외
  * @throws Exception
  */
 public static boolean chkInt(String iStr) throws Exception{
 
  if(!Common.chkNull(iStr)){
         return false;
     }

  if(iStr.indexOf(Constant.MINUS) != 0) return Common.chkPositiveInt(iStr.substring(0));
 
  return Common.chkPositiveInt(iStr.substring(1));
 
 
 }

일본어로 프로젝트를 진행하시는 분들은 참고하세여.
일본어가 들어 있는 스트링이 전각으로 되어 있는지 반각으로 되어 있는지 확인합니다.

참고) 아래의 메소드는 static으로 되어 있습니다.
      필요에 따라 바꿔서 사용하세여.
      이원영님께서 만드신 JDF에도 반영시켜도 좋을 듯 싶군여~ ㅋㅋ

(소스)----------------------------------------------------------------------------
    /**
     * 대상문자열(strTarget)이 전각문자로 구성되어 있는지 확인한다.
     *
     * @param strTarget 전각여부를 확인할 문자열
     * @return 전각문자만으로 구성된 문자열일 경우 true반환. 아니면 false
     */
    public static boolean isFullWord(String strTarget)
{ byte[] byteArray = null; byteArray = strTarget.getBytes();
for(int i = 0; i < byteArray.length; i++)
{ if((byteArray[i] >= (byte)0x81 && byteArray[i] <= (byte)0x9f) || (byteArray[i] >= (byte)0xe0 && byteArray[i] <= (byte)0xef))
{ if((byteArray[i+1] >= (byte)0x40 && byteArray[i+1] <= (byte)0x7e) || (byteArray[i+1] >= (byte)0x80 && byteArray[i+1] <= (byte)0xfc))
{ i++; }
else
{ return false; } }
else
{ return false; } }
return true; } /** * 대상문자열(strTarget)이 반각문자로 구성되어 있는지 확인한다. * * @param strTarget 반각여부를 확인할 문자열 * @return 반각문자만으로 구성된 문자열일 경우 true반환. 아니면 false */ public static boolean isHalfWord(String strTarget)
{ byte[] byteArray = null; byteArray = strTarget.getBytes();
for(int i = 0; i < byteArray.length; i++)
{ if((byteArray[i] >= (byte)0x81 && byteArray[i] <= (byte)0x9f) || (byteArray[i] >= (byte)0xe0 && byteArray[i] <= (byte)0xef))
{ if((byteArray[i+1] >= (byte)0x40 && byteArray[i+1] <= (byte)0x7e) || (byteArray[i+1] >= (byte)0x80 && byteArray[i+1] <= (byte)0xfc))
{ return false; } } }
return true; } 소프트웨어 기술의 강국! 우리나라 만세~ ^^
 
출처: http://www.javaservice.net/~java/bbs/read.cgi?m=resource&b=javatip&c=r_p&n=1016839954&k=전각&d=tb#1016839954

'프로그래밍 > Library' 카테고리의 다른 글

[펌] 자바로 엑셀 때려잡기!!  (0) 2007.11.28
[펌] 각종 제어  (0) 2007.11.28
[펌] 반각문자를 전각문자로  (0) 2007.11.28
[펌] 전각/반각 체크  (0) 2007.11.28
PDF 파일 만들기 - fop 0.93  (0) 2007.11.28

스크립트로 하는 것이라면..


//반각문자를 전각문자로
function convert2ByteChar(x_char) {
 var x_2byteChar = "";
 var c = x_char.charCodeAt(0);
 if ( 32 <= c && c <= 126 ) {
  if (c == 32) {
   x_2byteChar = unescape("%uFFFC");
  } else {
   x_2byteChar = unescape("%u"+gf_DecToHex(c+65248));
  }
 }
 return x_2byteChar;
}

[펌] 전각/반각 체크

프로그래밍/Library 2007. 11. 28. 11:02 Posted by galad

일본어에는 전각/반각이라는 것이 있다. 전각은 한문자를 표현하는데 2바이트를 사용하는 것, 반각은 영문자와 같이 1바이트를 사용하는 것. 업무시스템 수정사항중 전각/반각입력을 못하게해야되는 요구사항이 생겼다. 자바스크립트로 해결할 요량으로 구글링.


<SCRIPT LANGUAGE="javascript">

/** * 全角であるかをチェックします。 * * @param チェックする値 * @return ture : 全角 / flase : 全角以外 */

function checkIsZenkaku(value)
{
  for (var i = 0; i < value.length; ++i)

  {

    var c = value.charCodeAt(i);

    // 半角カタカナは不許可

    if (c < 256 || (c >= 0xff61 && c <= 0xff9f))

    {

      return false;

    }

  }

  return true;

}

 

function check()

{

  if (checkIsZenkaku(document.f.txt.value))

  {

    alert("指定の文字全ては全角です");

  }

  else

  {

    alert("指定の文字に全角以外の文字が含まれています");

  }

}

</SCRIPT>

 

or

 

<script language="javascript">

<!--//

/** JavaScript 入力文字列内に全角文字が含まれていたら警告する **/

function Check()

{  

  var str = document.frmForm.txtText.value;  

  for(i=0;i<str.length;i++)  

  {   

    if(escape(str.charAt(i)).length>=4)   

    {    

      alert("全角文字が含まれています");    

      document.frmForm.txtText.value = "";    

      return;   

    }  

  }  

  alert("全角文字は含まれていません");

}

//-->

</script>

 

 

 

이런 소스를 적용해보았다.
뭐..잘 동작하나, 업무시스템과 클라이언트가 전부 IE란 점을 착안. IME를 불가능하도록 하는 방법으로 간단히 해결.

<INPUT TYPE = text STYLE = "ime-mode:disabled" >

PDF 파일 만들기 - fop 0.93

프로그래밍/Library 2007. 11. 28. 11:02 Posted by galad

http://blog.naver.com/dmsl01/80033371267


http://blog.naver.com/dmsl01/80033368280


http://blog.theple.com/parkhs76/103.html




1. 폰트파일(nGulim.ttf)를 폰트 매트릭스 파일(nGulim.xml)로 바꾼다

java -cp D:\work\FopToPdf\WebContent\WEB-INF\lib\fop.jar;D:\work\FopToPdf\WebContent\WEB-INF\lib\avalon-framework.jar;D:\work\FopToPdf\WebContent\WEB-INF\lib\commons-logging.jar;D:\work\FopToPdf\WebContent\WEB-INF\lib\commons-io.jar org.apache.fop.fonts.apps.TTFReader D:\Temp\fop\nGulim.ttf D:\Temp\fop\nGulim.xml


2. 폰트파일과 폰트매트릭스 파일을 컨피그에 등록.

<font metrics-url="D:\Temp\fop\nGulim.xml" embed-url="D:\Temp\fop\nGulim.ttf" kerning="yes">
    <font-triplet name="NewGulim" style="normal" weight="normal"/>
    <font-triplet name="NewGulim" style="normal" weight="bold"/>
</font>

3. 컨피그를 사용하겠다고 알림

// 유저가 설정한 컨피그 파일을 사용한다. 밑에서 유저 에이전트를 생성하기 전에 컨피그 파일을 설정해야 폰트 등이 적용된다
fopFactory.setUserConfig(new File("d:/Temp/fop/mycfg.xml")); 


FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

.

.

.


4. fo 파일을 EUC-KR로 사용, 사용할 폰트를 설정

<?xml version="1.0" encoding="EUC-KR"?>
<fo:root font-family="NewGulim" font-size="12pt" font-style="normal" font-weight="normal" text-align="center" xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <fo:layout-master-set>
    <fo:simple-page-master master-name='simpleA4' page-height='29.7cm' page-width='21cm' margin-top='2cm' margin-bottom='2cm' margin-left='2cm' margin-right='2cm'>
      <fo:region-body/>
    </fo:simple-page-master>
  </fo:layout-master-set>
  <fo:page-sequence master-reference='simpleA4'>
    <fo:flow flow-name='xsl-region-body'>
      <fo:block>안녕</fo:block>
    </fo:flow>
  </fo:page-sequence>
</fo:root>

가깝고도 먼 이웃 만들기「트랙백」
블로그는 네트워크 상에서 자신의 정체성을 나타낼 수 있는 좋은 표현 수단입니다. 블로그간 커뮤니케이션 수단에는 꼬리말, 트랙백, 핑백 등이 있는데, 이 중 트랙백은 기존의 게시판 활용에서 볼 수 없었던 새로운 의견 표현 방법과 다양한 활용 가능성을 제공해 관심을 끌고 있습니다.
박병권 (다음커뮤니케이션 카페팀 엔지니어) 참여
우리나라에서는 작년부터 1인 미디어 성격의 블로그 서비스들이 유행처럼 생겨났지만 무언가 빠진 듯한 느낌이 들었습니다. 사실 달력, 꼬리말, 카테고리 이러한 것들만으로 사용자들에게 기존의 게시판과의 차별성을 이해시키기는 힘듭니다. 개인의 정체성을 네트워크를 통해 표출하는 것에 정답이 있는 건 아니지만 기존의 블로그들은 더 ‘블로그답게’ 변해야 할 필요가 있었습니다. 그래서인지 요즘에는 트랙백, 관련 글 등의 이름으로 트랙백이 지원되는 블로그 서비스들이 많이 등장했습니다.

‘블로그다운’ 의사소통 방식
블로그란 사람입니다. 다만 존재하는 공간이 다를 뿐입니다. 우리는 실제 세계에 살고 있지만 블로그는 네트워크 상에서 살고 있습니다. 제가 블로그를 쓸 때면 가끔 애니메이션 ‘공각기동대’에 나오는 “네트워크로 다이브(dive)한다”라는 말이 떠오릅니다. 네트워크로 다이브해서 나의 정체성을 표현하고 네트워크에 다이브해서 들어온 다른 블로거들을 만나는게 블로깅(blogging)이라고 생각합니다.

구체적으로 말하자면 자신의 정체성을 표현한다는 것은 대개 글이 되겠지만 사진, 음악, 동영상 등 추상적인 멀티미디어로도 가능합니다. 기존의 웹 게시판을 통해 자신의 목소리를 표현해 온 네티즌들에게는 자신의 정체성을 표현하는 것은 자연스럽게 받아들일 수 있는 개념입니다.

사실 자신의 정체성을 표현하는 것보다 더 중요한 것이 링크입니다. 여기서 링크라고 말한 것은 단순히 연결된 상태만을 나타내는 것이 아니라 연결된 상태 위에서 이루어지는 상호 작용까지 포함한 말입니다. 사회에서 사람이 혼자 살 수 없듯이 네트워크상에서 블로그는 혼자 존재할 수 없고 다른 블로그들과 링크를 만들게 됩니다.

사람들은 말, 글 또는 몸짓 등을 매개물(medium)로 자신을 표현하고 다른 사람들과의 링크를 만들어 가지만 블로그는 네트워크라는 아직은 미숙한 사회에서 링크를 만들어가야 합니다. 이러한 블로그간의 링크 역할을 해줄 수 있는 매개물은 꼬리말(comment), 트랙백(trackback), 핑백(pingback) 등이 있습니다. 꼬리말의 경우 워낙 익숙한 표현 방법이라 따로 설명이 필요 없을 것이고 핑백은 다른 블로거의 포스팅(posting, 블로그에 쓰여진 하나의 글을 의미함)을 링크로써 인용한 경우 원래의 포스팅에 대해 링크가 걸렸다는 것을 알려주는 기능입니다.

이 글에서 설명할 내용인 트랙백은 서로 다른 웹 사이트간의 알림(notification)을 가능하게 해주는 기능으로써 핑백과 비슷하게 원래의 포스팅에 대해 링크를 알려주긴 하지만 역으로 자신의 의견을 원래의 포스팅에 보낼 수 있다는 점이 특징입니다.

트랙백의 아이디어는 매우 간단하지만 아직도 몇몇 유명 블로그에 ‘트랙백 테스트’, ‘트랙백이 뭐지?’ 등의 트랙백이 들어오는 것을 보면 처음 접하는 사람들이 이해하기는 쉽지 않은 것 같습니다. 필자 또한 처음 접할 당시 무엇에 쓰는 것인지 무슨 의미를 가지는 것인지 알아내기 위해 꽤나 고민했습니다.

트랙백의 원리
트랙백의 핵심은 매우 간단합니다. 누군가의 글에 대해 자신의 블로그에 의견을 피력하면서 그 글에 대해 “당신이 쓴 글에 대해 제가 의견을 썼습니다. 당신이 관심을 가질 것 같아서 일부 내용과 함께 알려드립니다”라고 알려주는 것이 바로 트랙백입니다. 트랙백을 사용할 때와 그렇지 않을 때 어떻게 커뮤니케이션이 이루어지는지 예를 들어 보겠습니다.

◆ 트랙백을 사용하지 않은 경우
디지털 카메라(이하 디카) 커뮤니티 사이트에서 활동하고 있는 A와 B가 있습니다. 어느 날 A는 B가 써놓은 글을 보게 되고 B가 자신이 가지고 있는 디카와 동일 기종을 가지고 있고 그 디카에 대한 활용팁 등을 적어 놓은 것을 보게 됩니다. 이 글을 본 A는 B의 글이 상당히 유용하지만 자신이 알고 있는 활용팁이 몇 가지 빠진 것을 보고 답글 또는 꼬리말 형식으로 B의 글을 보충해줍니다.

◆ 트랙백을 사용한 경우
디카에 대해 관심이 있는 A와 B가 각자의 블로그를 쓰고 있습니다. 어느 날 A는 B의 블로그에서 자신이 가지고 있는 디카와 동일 기종에 대한 활용팁을 적은 글을 보게 됩니다. A는 B의 글에 대해 꼬리말을 달아 자신만이 알고 있는 부분을 보충할 수도 있지만 그 글의 트랙백 URL을 찾아서 자신의 블로그에 나머지 활용팁을 적은 후 B가 자신의 글에 대해 관심을 가질 거라고 생각하고 B의 글에 대해 트랙백 핑을 보냅니다.

이 두 가지 예제는 웹 사이트에 올라온 글에 대해 트랙백을 사용한 경우와 사용하지 않은 경우 그 글에 대해 취할 수 있는 한 가지 행동 예를 보여 줍니다. 트랙백을 사용하지 않은 경우에 비해 트랙백을 사용한 경우가 가지는 가장 큰 차이점은 하나의 웹 사이트 공간(블로그)을 초월한 A와 B 사이의 링크가 만들어진다는 점과 A는 자신의 공간에서 자신의 의견을 독립적으로 표현할 수 있다는 것입니다.

프로토콜 명세
트랙백의 프로토콜은 매우 간단합니다. HTTP의 POST 메쏘드를 통해서 몇 가지 파라미터(매개변수)를 보내고 XML로 된 응답으로 성공, 실패 여부만 판단해주면 됩니다.

트랙백 핑 보내기
트랙백은 기본적으로 HTTP를 이용해서 보내게 됩니다. Trackback 1.0에서는 GET 메쏘드를 이용해서 보내게 되어있지만 현재 쓰이는 1.1에서는 POST 메쏘드를 이용하게 되어 있습니다. Content-Type request header와 url이란 이름의 파라미터는 필수 요소입니다. Content-Type은 항상 appli cation/x-www-form-urlencoded이어야 하고 파라미터의 charset을 다음과 같이 추가로 지정해줄 수도 있습니다.

[표 1] POST request body로 보낼 파라미터  
파라미터명 설명
titile 트랙백으로 전송되는 글 제목
excerpt 트랙백으로 전송되는 글의 일부분. 전송되는 글이 무엇인지 알 수 있을 정도의 양이면 된다. 보통 글자수로 잘라서 보내는데 movabletype에서는 255자가 넘는 경우 252자까지 자르고 "..."을 붙여서 보낸다. 트랙백을 위한 excerpt를 따로 작성하여 보내도 된다.
url 트랙백으로 전송되는 글의 permalink
blog_name 트랙백으로 전송되는 글이 쓰여진 블로그 이름

◆ Content-Type
application/x-www-form-urlencoded 또는 Content-Type: application/x-www-form-urlencoded; charset=EUC-KR 또는 Content-Type: application/x-www-form-urlencoded; charset=UTF-8
POST request body로 보낼 파라미터들은 [표 1]과 같습니다. 이 트랙백 요청을 보내는 것을 ‘트랙백 핑을 보낸다’라고 합니다. 예를 들어 어떤 글이 http://www.anyblog.com/ tb.cgi/7과 같은 트랙백 URL을 가지고 있다고 한다면 다음과 같은 HTTP 요청으로 그 글에 트랙백 핑을 보낼 수 있습니다. 여기서 트랙백 URL이란 트랙백 핑을 받는 프로그램이 실행되는 URL을 말합니다.

POST /tb.cgi/7 http/1.1
Host: www.anyblog.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 96

title=hello&excerpt=trackback%20test&url=http://www.mtgear.net/archieve/xx.html&blog_name=mtgear

알다시피 HTTP의 POST 메쏘드는 request body를 통해서 파라미터를 전달하고 HTTP request에서 request body가 있을 경우 Content-Length가 request body를 지정해줘야 합니다. 여기서 보내는 사람의 블로그는 mtgear(http:// mtgear .net)이고 제목이 hello라는 글을 쓰면서 글 내용의 발췌인 ‘trackback test’를 http://www.anyblog.com/tb.cgi/7이란 트랙백 URL을 가진 글에 트랙백 핑을 보내는 것입니다. 트랙백 핑을 보낸 후 성공한 경우에는 다음과 같은 응답을 받게 됩니다.

lt;?xml version=”1.0” encoding=”iso-8859-1”?><response><error>0</error> </response>

실패한 경우에는 다음과 같은 응답을 받게 되고 메시지의 텍스트 요소(text element)를 통해서 오류의 원인을 알 수 있습니다.

<?xml version=”1.0” encoding=”iso-8859-1”?><response><error>1</error> <message>The error message</message></response>

트랙백 핑을 보내는 프로그램 작성
그럼 실제로 트랙백 핑을 보내는 프로그램을 만들어 봅시다. 언어는 자바를 사용하겠습니다. 먼저 트랙백 자체를 나타내는 클래스를 만듭니다. 이 클래스에서는 트랙백을 통해서 보내지는 4가지 파라미터의 내용을 담고 있습니다.

 트랙백 자체를 나타내는 클래스
package bk.trackback;

public class Trackback {

  private String title;
  private String excerpt;
  private String url;
  private String blogname;

  public String getBlogname() {
    return blogname;
  }

  public void setBlogname(String blogname) {
    this.blogname = blogname;
  }

  public String getExcerpt() {
    return excerpt;
  }

  public void setExcerpt(String excerpt) {
    this.excerpt = excerpt;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }
}

그리고 실제로 트랙백 핑을 보내는 클래스는 다음과 같습니다.

 트랙백 핑을 보내는 클래스
package bk.trackback;

import java.io.IOException;
import java.net.MalformedURLException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;

public class TrackbackPing {

  private String trackbackURL;

  public TrackbackPing(String trackbackURL) throws MalformedURLException {
    this.trackbackURL = trackbackURL;
  }

  public boolean send(Trackback trackback, String charset) throws
    IOException {
    HttpClient client = new HttpClient();
    PostMethod post = new PostMethod(trackbackURL);
    if (charset == null) {
      post.setRequestHeader(“Content-Type”, “application/x-www-form-
        urlencoded;”);
    } else {
      post.setRequestHeader(“Content-Type”, “application/x-www-form-
        urlencoded; charset=” + charset);
    }
    post.addParameter(“url”, trackback.getUrl());
    post.addParameter(“title”, trackback.getTitle());
    post.addParameter(“blog_name”, trackback.getBlogname());
    post.addParameter(“excerpt”, trackback.getExcerpt());
     int statuscode = client.executeMethod(post);

   return statuscode == 200 && (post.getResponseBodyAsString().indexOf
     (“<error>0</error>”) > 0);
  }
}

여기서 HTTP 부분은 직관적으로 이해하기 쉽게 하기 위해 jakarta commons의 HttpClient를 이용했습니다. send() 부분에서 application/x-www-form-urlencoded으로 Content-Type을 설정하고 request body 부분의 character set을 설정해주는 부분과 트랙백으로 전송될 4가지 파라미터를 설정하는 부분이 있습니다.

트랙백 핑에 대한 응답으로 HTTP response code인 200 OK를 받는지와 response body 부분에 <error>가 0인지를 검사해서 트랙백 핑의 성공 여부를 알 수 있습니다. 그러면 앞의 코드가 실제로 작동하는지 테스트해 봅시다. 테스트 코드는 junit을 이용해 작성했습니다.

 코드가 작동하는지 테스트
package bk.trackback;

import java.io.IOException;

import junit.framework.TestCase;

public class TrackbackPingTest extends TestCase {

  public void testSend() throws IOException {
    TrackbackPing ping = new TrackbackPing(“http://www.anytrackbackurl.com/            
      tb/tb.cgi/xx”);
    Trackback trackback = new Trackback();
    trackback.setBlogname(“테스트”);
    trackback.setExcerpt(“안녕하세요”);
    trackback.setTitle(“그럼”);
    trackback.setUrl(“http://www.bk.com”);

    assertTrue(ping.send(trackback, “EUC-KR”));
  }
}

앞의 테스트에서 TrackbackPing의 constructor에 주는 값은 트랙백 URL이어야 합니다. 이 테스트가 성공한다면 실제로 트랙백이 보내진 것입니다.

트랙백 핑 받기
트랙백 핑을 받는 입장에서는 트랙백을 받아 자유롭게 저장해서 나중에 보여주기만 하면 되고 별다른 제약 사항은 없습니다. 다만 받는 입장에서는 글을 사람이 올리는게 아니라 다른 웹 사이트에서 올려진다는 것이 다를 뿐입니다.

트랙백 핑 목록 보기
어떤 글에 보내진 트랙백들을 보려면 HTTP GET 메쏘드로 트랙백 URL과 함께 __mode=rss 파라미터를 같이 주면 됩니다. 트랙백 핑이 POST 요청으로 보내지므로 GET 요청만으로도 트랙백 목록을 보여줘도 됩니다. 하지만 트랙백이 1.0에서 1.1로 올라오면서 GET에서 POST로 변한 것이기 때문에 아직은 GET 요청으로 트랙백 핑을 보내는 경우가 있다는 가정 하에서 앞의 파라미터를 따로 사용하는 것입니다. 하지만 추후에 발표될 트랙백 표준에서는 GET 요청만으로 트랙백 목록을 가져오도록 명세가 변한다고 합니다.

URL 자동 찾기, 오토 디스커버리
트랙백을 보내는 입장에서 트랙백 URL을 일일이 찾아 복사하기와 붙여넣기 후 트랙백 핑을 보낸다는게 꽤나 귀찮은 일입니다. 블로그 글을 읽으면서 그 글에 대한 트랙백 URL을 자동으로 찾아주는 기능이 있는데 그게 바로 오토 디스커버리(auto discovery)입니다.

최근 포털 사이트에서 서비스하는 블로그들을 보면 트랙백 또는 관련 글 등의 이름으로 트랙백이 지원되는데 다른 사람의 블로그에서 글을 읽다가 ‘관련 글 쓰기’ 등의 버튼을 누르고 글을 쓰면 그 글이 자동으로 자신의 블로그에 등록되고 읽고 있던 글에 트랙백 핑이 전송됩니다. 읽고 있던 글의 트랙백 URL을 몰라도 이렇게 트랙백 핑을 전송할 수 있도록 해주는 기능이 바로 오토 디스커버리입니다.

이는 트랙백 URL을 찾도록 HTML에 임베드된 RDF와 그 RDF에서 트랙백 URL을 찾아내는 부분으로 되어 있습니다. 트랙백 핑을 보내는 입장, 즉 트랙백 클라이언트는 블로그 글과 함께 제공되는 RDF에서 해당 글의 트랙백 URL을 추출해 냅니다. 이렇게 찾아낸 트랙백 URL은 전용 클라이언트 또는 웹 프로그램을 통해서 트랙백을 보내는데 이용됩니다. RDF 내에서 트랙백에 관련된 태그는 [표 2]와 같은 두 가지가 있습니다.

[표 2] RDF에서 트랙백에 관련된 태그  
trackback:ping 트랙백을 받을 수 있는 URL을 나타내며 오직 하나만 있다.
trackback:about 현재 글에 전송된 트랙백 핑을 보낸 곳의 트랙백 URL이 나타나며 여러개가 있을 수 있다.
예를 들어 A와 B가 C에 트랙백 핑을 전송했다면 C의 trackback:about에 A와 B의 트랙백 URL이 나타난다.

다음은 실제 블로그 글에 포함된 RDF의 일부 샘플이고 여기에서 trackback:ping으로 된 부분 http://mtgear.net/mt/ mt-tb.cgi/200이 이 글에 대한 트랙백 핑을 받을 트랙백 URL입니다.

 실제 블로그 글에 포함된 RDF
<!--
<rdf:RDF xmlns:rdf=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
  xmlns:trackback=”http://madskills.com/public/xml/rss/module/trackback/”
  xmlns:dc=”http://purl.org/dc/elements/1.1/”><rdf:Description
  rdf:about=”http://mtgear.net/archives/000207.php”
  trackback:ping=”http://mtgear.net/mt/mt-tb.cgi/200”
  dc:title=”천장에서 찍은 내모습(?)”
  dc:identifier=”http://mtgear.net/archives/000207.php”
  dc:subject=”끄적끄적”
  dc:description=” 진짜루?...”
  dc:creator=”wanderer”
  dc:date=”2004-04-05T22:10:08+09:00” /></rdf:RDF>
-->

트랙백의 활용
트랙백은 원격 꼬리말과 컨텐츠 수집 등과 같은 방법으로 많이 활용되지만 그 이외에 여러가지 방법으로 활용될 수 있습니다.

원격 꼬리말
원격 꼬리말(Remote commenting)은 어떤 글에 대한 자신의 의견을 그 글이 있는 곳에 답글이나 꼬리말 등으로 남기는 대신 자신의 사이트에 관련된 글을 남기고 그 글을 트랙백 핑으로 보내는 것입니다. 이렇게 보내진 트랙백 핑은 외부 사이트에 쓰여진 글이 마치 자신의 사이트에 꼬리말처럼 남겨지는 효과를 줍니다. 또한 이렇게 트랙백으로 자신의 URL을 보냄으로써 블로그간의 링크를 형성할 수도 있습니다.

컨텐츠 수집
http://www.blogkorea.org가 트랙백의 컨텐츠 수집(content aggregation)을 적절히 활용한 예라 할 수 있는데 트랙백을 받는 주체가 특정 글이 아닌 일종의 추상적인 카테고리가 될 수 있다는 것을 응용한 기술입니다. 예를 들어 내가 관심있는 주제에 대한 카테고리를 만들고 그 카테고리에 대한 트랙백 URL을 공개한다면 그 주제에 대해 공통 관심을 가진 사람들이 자신의 블로그에 글을 남기면서 그 카테고리에 트랙백 핑을 보내줄 것입니다. 이렇게 보내진 트랙백 핑들은 같은 관심사에 대한 글일 것이므로 자동으로 관련된 글들, 즉 컨텐츠를 수집할 수 있습니다.

적극적으로 정보를 전달하는 트랙백
자신의 의견을 표현하고 남이 읽어주기를 바라는 기존의 피동적인 웹 이용 행태와 비교해보면 나의 의견을 읽도록 적극적으로 강요(?)할 수 있는 트랙백은 자기 주장이 뚜렷하고 솔직해지는 요즈음의 사회 흐름에 잘 부합할 수 있는 기능입니다. 그리고 앞의 두 가지 트랙백 이용 사례가 있지만 정보의 흐름을 적극적으로 컨트롤할 수 있다는 측면은 더 나은 응용 사례를 만들어낼 수 있습니다. 더군다나 이런 생각이 표준으로 어느 곳에서나 활용될 수 있다는 것이 얼마나 멋집니까! @

* 이 기사는 ZDNet Korea의 자매지인 마이크로소프트웨어에 게재된 내용입니다.

출처 : Tong - '겨울나기'님의 RSS통

본 문서에서는 HTTP 헤더를 사용하여 Internet Explorer에서 웹 페이지의 캐싱을 제어하는 방법에 대해 설명합니다.

Microsoft Internet Information Server(IIS)를 사용하면 특정 ASP(Active Server Pages) 페이지의 맨 앞에 다음 스크립트를 사용하여 휘발성이 매우 높거나 매우 중요한 페이지를 쉽게 표시할 수 있습니다.
<% Response.CacheControl = "no-cache" %>
<% Response.AddHeader "Pragma", "no-cache" %>
<% Response.Expires = -1 %>
				

위로 가기

추가 정보

만료 및 Expires 헤더

모든 웹 서버에서는 모든 웹 페이지의 만료에 대한 구성을 사용하는 것이 좋습니다. 웹 서버가 요청 클라이언트에 반환되는 모든 리소스에 대해 HTTP Expires 응답 헤더를 통해 만료 정보를 제공하지 않으면 문제가 생길 수 있습니다. 대부분의 브라우저와 중간 프록시는 현재 이 만료 정보를 적용하고 이 정보를 사용하여 네트워크 상의 통신 효율을 향상시킵니다.

서버의 특정 파일을 클라이언트에서 업데이트해야 하는 가장 적절한 시간을 지정하려면 항상 Expires 헤더를 사용해야 합니다. 페이지가 정기적으로 업데이트되는 경우에는 다음 업데이트 기간이 가장 효율적인 응답입니다. 예를 들어, 인터넷에서 매일 오전 5시에 업데이트되는 일간 뉴스 페이지의 경우 이 뉴스 페이지의 웹 서버는 값이 다음 날 오전 5시인 Expires 헤더를 반환해야 합니다. 이 값이 반환되면 브라우저는 페이지가 실제로 변경될 때까지 웹 서버에 다시 연결할 필요가 없습니다.

변경될 것 같지 않은 페이지에는 대략 1년의 만료 날짜가 표시됩니다.

웹 서버에는 바로 변경될 정보가 들어 있는 휘발성 페이지가 하나 이상 있는 경우가 많습니다. 서버에서는 이러한 페이지의 Expires 헤더 값을 "-1"로 표시합니다. 사용자가 이후에 요청하면 Internet Explorer는 대개 조건부 If-Modified-Since 요청을 통해 해당 페이지를 업데이트하기 위해 웹 서버에 연결합니다. 그러나 해당 페이지는 디스크 캐시("임시 인터넷 파일")에 남아 있으며, 뒤로 단추와 앞으로 단추로 탐색 기록에 액세스하는 경우나 브라우저가 오프라인 모드에 있는 경우 원격 웹 서버에 연결하지 않고 적절한 상황에서 사용됩니다.

위로 가기

Cache-Control 헤더

특정 페이지는 휘발성이 매우 높거나 매우 중요해서 디스크 캐싱이 필요하지 않습니다. 이를 위해 Internet Explorer는 HTTP 1.1 Cache-Control 헤더를 지원합니다. 이 헤더는 HTTP 1.1 서버에서 캐시 값을 지정하지 않은 경우 특정 웹 리소스의 모든 캐싱을 방지합니다.

브라우저가 웹 서버에 다시 연결할 수 있을 때까지 캐시에 없는 페이지는 액세스할 수 없기 때문에 반드시 필요한 경우에만 서버에서 Cache-Control 헤더를 사용해야 합니다. 대부분의 경우에는 "Expires: -1"을 사용하는 것이 좋습니다.

위로 가기

Pragma: No-Cache 헤더

이전 HTTP 1.0 서버에서는 Cache-Control 헤더를 사용할 수 없다는 문제가 있습니다. HTTP 1.0 서버와의 호환성을 위해 Internet Explorer는 HTTP Pragma: no-cache 헤더의 특수한 사용을 지원합니다. 클라이언트가 보안 연결(https://)을 통해 서버와 통신하고 서버가 응답과 함께 Pragma: no-cache 헤더를 반환하면 Internet Explorer는 응답을 캐시하지 않습니다.

그러나 Pragma: no-cache 헤더는 이를 위해 만들어진 것은 아닙니다. HTTP 1.0과 1.1 사양에 따라 이 헤더는 응답이 아니라 요청 컨텍스트에서만 정의되며, 실제로는 중요한 특정 요청이 대상 웹 서버에 도달하지 못하게 할 수 있는 프록시 서버용으로 만들어졌습니다. 이후의 응용 프로그램에서는 Cache-Control 헤더가 캐싱을 제어하는 적절한 수단이 될 것입니다.

위로 가기

HTTP-EQUIV META 태그

HTML 페이지에서는 HTML 문서에서 특정 HTTP 헤더를 지정하는 META 태그의 특수한 HTTP-EQUIV 형식이 허용됩니다. 다음은 Pragma: no-cache와 Expires: -1을 모두 사용하는 간단한 HTML 페이지의 예입니다.
<HTML><HEAD>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="-1">
</HEAD><BODY>
</BODY>
</HTML>
				
Pragma: no-cache는 보안 연결을 통해 사용되는 경우에만 캐싱을 방지합니다. Pragma: no-cache META 태그는 비보안 페이지에서 사용되는 경우 Expires: -1과 동일하게 처리됩니다. 페이지는 캐시되지만 즉시 만료되는 것으로 표시됩니다.

Cache-Control META HTTP-EQUIV 태그는 무시되고 Internet Explorer 버전 4 또는 5에서 효과를 나타내지 않습니다. Cache-Control을 사용하려면 위의 Cache-Control 섹션에 설명된 HTTP 헤더를 사용하여 이 헤더를 지정해야 합니다.

표준 HTTP 헤더가 META 태그보다 훨씬 많이 사용됩니다. META 태그는 일반적으로 HTML HEAD 섹션의 맨 위에 나타나야 합니다. 또한 Pragma HTTP-EQUIV META 태그에는 알려진 문제점이 적어도 하나 있습니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
222064 (http://support.microsoft.com/kb/222064/) "Pragma: No-cache" 태그를 사용해도 페이지가 캐시될 수 있다

위로 가기

서버의 캐싱 옵션

ASP 페이지 이외의 페이지에서 Cache-Control 헤더를 사용해야 하는 경우에는 서버 구성의 옵션을 사용하여 이 헤더를 자동으로 추가해야 할 수 있습니다. HTTP 헤더를 특정 디렉터리의 서버 응답에 추가하는 프로세스는 해당 서버 설명서를 참조하십시오. 예를 들어, IIS 4에서는 다음과 같이 하십시오.
인터넷 서비스 관리자를 호출합니다.
컴퓨터와 서비스 트리를 사용하여 기본 웹 서버(또는 문제의 웹 서버)를 열고 Cache-Control 헤더가 필요한 콘텐츠가 들어 있는 디렉터리를 찾습니다.
해당 디렉터리의 등록 정보 대화 상자를 표시합니다.
HTTP 헤더 탭을 선택합니다.
사용자 지정 HTTP 헤더 그룹에서 추가 단추를 누른 다음 헤더 이름으로 "Cache-Control"을 추가하고 헤더 값으로 "no-cache"를 추가합니다.
전체 웹 서버에서 이 헤더를 사용하는 것은 좋지 않습니다. 클라이언트에서 절대로 캐시되지 않아야 하는 콘텐츠에만 이 헤더를 사용하십시오.

위로 가기

문제 검사 목록

본 문서에 나와 있는 방법을 적용했는데도 Internet Explorer와 캐싱에 문제가 있는 경우에는 Microsoft에 기술 지원을 요청하기 전에 다음과 같은 유용한 검사 목록을 단계별로 검토하십시오.
Cache-Control 헤더를 ASP "Response.CacheControl" 속성과 함께 사용하거나 반환된 HTTP 헤더를 통해 사용하고 있습니까? 이것은 Internet Explorer에서 캐싱을 완전히 방지하는 유일한 방법입니다.
Internet Explorer 4.01 서비스 팩 2 이상을 사용하고 있습니까? 이전 버전의 브라우저에서 캐싱을 완전히 방지하는 방법은 없습니다.
웹 서버에 HTTP 1.1이 설정되어 있고 Internet Explorer에 HTTP 1.1 응답을 반환 중인지 다시 확인했습니까? Cache-Control 헤더는 HTTP 1.0 응답에 적합하지 않습니다.
서버쪽에서 CGI/ISAPI/서블릿을 사용하는 경우, 특히 HTTP 헤더의 CRLF 종결과 관련하여 HTTP 1.1 사양을 정확하게 따르고 있습니까? 일반적으로 성능 면에서 Internet Explorer는 HTTP 1.1 사양을 위반하는 응답은 허용하지 않습니다. 이에 따라 대개 헤더가 무시되거나 예기치 않은 서버 오류 보고서가 만들어집니다.
HTTP 헤더의 철자가 정확합니까?

위로 가기

참조

자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
189409 (http://support.microsoft.com/kb/189409/) INFO: IIS 4.0에서 웹 페이지의 캐싱 제어
165150 (http://support.microsoft.com/kb/165150/) IIS와 IE에서 Pragma: No-cache를 사용하는 방법
HTTP/1.1에 대한 자세한 내용을 보려면 다음 웹 사이트를 방문하여 RFC 2616을 참조하십시오.
http://www.w3.org/Protocols/rfc2616/rfc2616.html (http://www.w3.org/Protocols/rfc2616/rfc2616.html)

Microsoft Internet Explorer cache issues

 

Internet Explorer implements caching for GET requests. Authors who are not familiar with HTTP caching expect GET requests not to be cached, or for the cache to be avoided as with the refresh button. In some situations, failing to circumvent caching is a bug. One solution to this is to use the POST request method, which is never cached; however, it is intended for non-idempotent operations.

Setting the "Expires" header to reference a date in the past will avoid caching of the response. Here is an example in PHP.

header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); 
// disable IE caching header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" ); 
header( "Cache-Control: no-cache, must-revalidate" ); header( "Pragma: no-cache" );
 

Caching may also be disabled, as in this Java Servlet example.

response.setHeader( "Pragma", "no-cache" ); 
response.addHeader( "Cache-Control", "must-revalidate" ); 
response.addHeader( "Cache-Control", "no-cache" ); 
response.addHeader( "Cache-Control", "no-store" ); 
response.setDateHeader("Expires", 0);
 

Caching disabled in this C# Handler example which forces all proxy caches to revalidate, prevents browser from caching and expires the response:

context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); 
context.Response.Cache.SetCacheability(HttpCacheability.NoCache); 
context.Response.Cache.SetNoStore(); 
context.Response.Cache.SetNoServerCaching(); 
context.Response.Cache.SetExpires(DateTime.Now); 
 

Alternatively, it is possible to force the XMLHttpRequest object to retrieve the content anyway, as shown in this example.

req.open( "GET", "xmlprovider.php" ); 
req.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" ); 
//This line doesn't work. 
req.send( null ); 

Another method is to add a random string on the end of the url in the query:

req.open( "GET", "xmlprovider.php?sid=" + Math.random());
 

This will ensure that the browser always gets a fresh copy.

It is important to note that these techniques should be used only if the caching is inappropriate. If such methods are used indiscriminately, poor performance with the application may result. Another better way around that may be sending an expired date/time header (or other relevant headers) to the client to gain benefits from caching while letting the client know that new data may be available, as generally it is better to obtain the performance benefits from caching to reduce processing time and bandwidth consumption.


대부분 간단히 요청주소에 현재 시간을 덧붙여서 전송합니다.
'action.php?t=1144760075' 이런식으로요.. 주소가 달라지니 캐시도 사용되지 않겠죠.

req.open("GET", "xmlprovider.php?hash=" + Math.random());
위와 같이 캐쉬를 하지않게 호출할때 마다 요청에 유일한 쿼리스트링을 포함함돠

서버 프로그램 개발시에 운영체제의 메모리,스레드,파일 디스크립터, 프로세스 최대 지원 개수를 점검하는 간단한 방법을 소개한다.

일반적으로

항목 원도우 Linux
메모리 4G Byte 2.0.x : 1G Byte
2.2.x : 2G Byte
2.4.x : 64G Byte
최대 스레드 제한없음 기본설정 : 1024
변경가능
파일 디스크립트 제한없음 기본설정 : 1024
변경가능

1. checkpf.jar 다운로드

2. 메모리 점검

  • Usage

    [www@ihelpers dist]$ java -cp ./checkpf.jar checkpf.MemConsumer 128
    Allocated : 12
    Max : 63
    Total : 14
    Left : 2
    Allocated : 24
    Max : 63
    Total : 25
    Left : 1
    Allocated : 36
    Max : 63
    Total : 43
    Left : 7
    Allocated : 48
    Max : 63
    Total : 63
    Left : 15
    Allocated : 60
    Max : 63
    Total : 63
    Left : 3
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
            at checkpf.MemConsumer.main(MemConsumer.java:34)
    [www@ihelpers dist]$ java -cp ./checkpf.jar checkpf.MemConsumer 64

    128MB 짜리 배열을 생성하면 에러가 발생한다. 아래와 같이 최대 메모리를 128MB로 잡아주면 에러 없이 생행이 될수 있습니다. 자바 Virtual Machine 의 메모리를 너무 크게 잡게 되면 디스크 스와핑과 Garagage Collection 이 많이 발생하여 서버의 성능이 현저하게 떨어지게 됩니다. 자바 VM 최대 메모리는 물리적 메모리 보다 작게 하고 프로그램에서는 VM 최대 메모리의 약 70% 이하로 사용하는 것이 좋습니다.

    [www@ihelpers dist]$ java -Xmx128m -cp ./checkpf.jar checkpf.MemConsumer 128
    Allocated : 12
    Max : 127
    Total : 14
    Left : 2
    Allocated : 24
    Max : 127
    Total : 25
    Left : 1
    Allocated : 36
    Max : 127
    Total : 43
    Left : 7
    Allocated : 48
    Max : 127
    Total : 75
    Left : 27
    Allocated : 60
    Max : 127
    Total : 75
    Left : 15

  • java options

[www@ihelpers dist]$ java -X
    -Xmixed           mixed mode execution (default)
    -Xint             interpreted mode execution only
    -Xbootclasspath:<directories and zip/jar files separated by :>
                      set search path for bootstrap classes and resources
    -Xbootclasspath/a:<directories and zip/jar files separated by :>
                      append to end of bootstrap class path
    -Xbootclasspath/p:<directories and zip/jar files separated by :>
                      prepend in front of bootstrap class path
    -Xnoclassgc       disable class garbage collection
    -Xincgc           enable incremental garbage collection
    -Xloggc:<file>    log GC status to a file with time stamps
    -Xbatch           disable background compilation
    -Xms<size>        set initial Java heap size
    -Xmx<size>        set maximum Java heap size
    -Xss<size>        set java thread stack size
    -Xprof            output cpu profiling data
    -Xfuture          enable strictest checks, anticipating future default
    -Xrs              reduce use of OS signals by Java/VM (see documentation)
    -Xcheck:jni       perform additional checks for JNI functions
    -Xshare:off       do not attempt to use shared class data
    -Xshare:auto      use shared class data if possible (default)
    -Xshare:on        require using shared class data, otherwise fail.

3. 파일 디스크립터

  • Usage

    [www@ihelpers dist]$ java -cp ./checkpf.jar checkpf.FDConsumer 2000
    ...

    File Descriptor : 1012
    File Descriptor : 1013
    File Descriptor : 1014
    File Descriptor : 1015
    java.io.IOException: Too many open files


    이에 대한 설정 변경 내용은 참고를 참조해 주십시요.

3. 참고

http://blog.naver.com/galad/140033728088

Fancy URL 사용을 위한 준비 (Tomcat + Spring + urlRewrite Filter)
PRIVATE/- Developer's Diary | 2006/10/26 01:03

 

펌.: http://steelheart.tistory.com/32

 

더 좋은 방법이 있는지는 모르겠다.
일단 삽질한 거니 기록으로 남겨둔다.

아무튼... 먼저 Fancy URL 이란?

/board.html?action=list&page=2 이런 일반적인 URL 형식 대신
/board/list/2 이런 형식의 확장자가 없고 쿼리 스트링이 없는 간단한 URL을 말한다.
(fancy url의 정확한 뜻은 모르지만 나는 이렇게 이해하고 있다)

태터툴즈의 /tt/tags/태터툴즈 (태터툴즈란 태그가 있는 글목록을 출력하는 url) 이런걸 생각하면 된다.



가장 간단한 방법은 Apache의 mod_rewrite 모듈을 사용하면 된다.
하지만 이 경우는 아파치가 없거나 있어도 mod_rewrite 모듈 사용이 불가능한 걸 가정했다.

확장자가 없는 매핑이라도 특별한 패턴이 있으면 간단하게 할 수 있다. 예를 들면

/board/list
/board/view
/board/write

이런 매핑을 처리하려면 다음처럼 하면 된다.

   <servlet-mapping>
       <servlet-name>lucy</servlet-name>
       <url-pattern>/board/*</url-pattern>
   </servlet-mapping>

하지만

/list
/view
/write

이런걸 매핑하려면?

   <servlet-mapping>
       <servlet-name>lucy</servlet-name>
       <url-pattern>/list</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
       <servlet-name>lucy</servlet-name>
       <url-pattern>/view</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
       <servlet-name>lucy</servlet-name>
       <url-pattern>/write</url-pattern>
   </servlet-mapping>

특별한 패턴이 없는 url이기 때문에 전부다 매핑해 줘야 한다.
이건 세개뿐이니까 그렇지 매번 늘어날 때마다 매핑해 줄수도 없다.
특히 어떤 url로 요청이 들어올지 미리 알 수 없는 경우는 난감하다.
예를 들면 /(username) 했을 경우 해당 사용자의 정보를 보여준다든지 하는 것


아무튼 글의 요점은 확장자가 없는 모든 요청을 dispatcher가 받도록 하고 싶다는 거다.
내가 알기로는... 현재의 servlet-mapping은... 아래와 같은 식이나

   <servlet-mapping>
       <servlet-name>lucy</servlet-name>
       <url-pattern>/*</url-pattern> <!-- 모든 요청을 처리하되 -->
      <url-pattern-exclude>*.*</url-pattern-exclude> <!-- 확장자 '.' 이 들어가면 안돼 -->
  </servlet-mapping>

또는 아래와 같은 형식으로는...

   <servlet-mapping>
       <servlet-name>lucy</servlet-name>
       <url-pattern>^/([^.]*)$</url-pattern> <!-- . 이 포함되지 않은 패턴 -->
  </servlet-mapping>

이렇게는 지원이 안되기 때문에;;; 조금 편법을 써야 했다.


일단 매핑은 아래처럼...

   <servlet-mapping>
       <servlet-name>lucy</servlet-name>
       <url-pattern>/app/*</url-pattern>
  </servlet-mapping>


이렇게 해당 /app/로 시작하는 모든 요청에 대해 처리하도록 한다.


근데 이것도 맘에 안 드는게 확장자 없이 매핑하는건 되지만
url을 항상 /app/로 요청해야 한다.
그다지 의미있는 url은 아닌데 항상 따라다니는 것도 보기 안좋고...


appfuse에서 발견한... urlrewrite filter를 사용한다.
(appfuse에서는 이런 목적으로 쓰진 않았지만...)


https://urlrewrite.dev.java.net/ 에서 urlrewrite filter 3.0 을 받아 lib 폴더에 넣는다.

/WEB-INF/urlrewrite.xml 을 작성


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">

<!-- https://urlrewrite.dev.java.net/manual/3.0 -->

<urlrewrite>

   <rule>
       <note>
           확장자가 없는 요청은 /app/* 요청이다.
           ex) /user/register -> /app/user/register
       </note>
       <from>^/([^.]*)$</from>
       <to type="forward">/app/$1</to>
   </rule>

</urlrewrite>



web.xml 에 url rewrite filter 등록


   <filter>
       <filter-name>urlRewriteFilter</filter-name>
       <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
       <init-param>
           <param-name>logLevel</param-name>
           <param-value>commons</param-value>
       </init-param>
   </filter>


   <filter-mapping>
       <filter-name>urlRewriteFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>


Spring Context XML 파일에서...

   <bean id="userMappings" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="mappings">
           <map>
               <entry key="/user/register" value-ref="userRegisterController"/>
               <entry key="/user/login" value-ref="userLoginController"/>
           </map>
       </property>


앞에 /app를 붙여 주지 않아도 된다... 물론 브라우저에서 요청시에도 /app를 안 붙여도 되고...

[펌] 가상주소 구현

프로그래밍/Library 2007. 11. 28. 10:55 Posted by galad
출처 블로그 > ASP
원본 http://blog.naver.com/jaupa/150013527996
블로그나 미니홈피,클럽등의 서비스를 제공함에 있어서 계정 사용자에게 가상적으로 주소를 발급하기 위한 방법입니다. 설명의 편의상 제가 개인적으로 운영하고 있는
http://nbloger.com/ 을 예로 들었습니다. 그리고 다음 두가지 형태로 분류하여 설명하도록 하겠습니다.
 
1. http://아이디.nbloger.com
 
이 형태는 윈도우즈 DNS 상의 편집을 필요로 합니다. 즉, * 라는 host 명을 가지는 호스트를 추가시켜 주셔야 합니다. 물론 기본적인 윈도우즈 DNS GUI 환경에서는  편집이 불가능합니다. 따라서 zone 파일을 직접 수정하셔야 합니다. zone 파일은 텍스트 형태로서 winnt/system32/dns 폴더도메인.dns 형태로 존재합니다. 예컨데 nbloger.com 이라는 zone 파일의 최하단에 다음과 같이 host 리스트들이 존재한다면..
;
;  Zone records
;
@                      A   IP
blog                  A   IP
www                 A   IP
 
최하단에 다음과 같이 추가하여 주십시오.
 
*                       A   IP
 
DNS를 새로고침 하신 이후 DNS의 MMC를 보시면 * 라는 host 가 추가된 것을 확인하실수 있습니다.
이제는 IIS에서 작업을 하셔야 합니다. IIS에서 웹사이트 추가후 웹사이트 등록정보에서 웹사이트 탭에 보시면 IP 적는란 오른쪽에 고급 이라는 버튼이 있습니다. 이 버튼을 클릭하신후 나타나는 창(고급 복수 웹사이트 구성)에서 아래와 같이 추가하여 주십시오.
 
IP : 해당 IP
포트 : 일반적으로 80
호스트헤더 이름 : 입력하지 마십시오.
 
지금까지 ID.도메인.com 형태의 접근을 해당 웹사이트로 연결시켜 주는 설정을 마쳤습니다. 마지막으로 ASP 상의 코딩이 필요합니다. 웹사이트 방문시 가장 처음 실행되는 default.asp에서 다음 예제를 참고하여 코딩하여 주십시오.
 
default.asp
<%
dim request_url,array_url
' 서버명을 받아온다. (접근자가 실제 입력한 주소)
request_url = Request.ServerVariables("SERVER_NAME")
' .을 기준으로 분리한다.
array_url = split (request_url,".")
 
if UBound (array_url) > 1 then
   ' ID.nbloger.com 형태로 host 명을 가지고 접근하는 경우
   ' array_url(0) 가 host 명 , 즉 회원 아이디입니다.
   Response.Redirect("mem.asp?blogid="&array_url(0))
else
   ' host 명 없이 nbloger.com 으로 접근한 경우
   Response.Redirect(해당 주소)
end if
%>
 
 
이 형태에서는 실제 서버상에 존재하지 않는 폴더이지만 가상적으로 하위 경로 주소를 구현합니다.
DNS 의 편집은 필요하지 않습니다. 다만 IIS 의 404 에러에 대하여 약간의 트릭을 사용하셔야 합니다.
404 에러란 웹사이트 접근자가 잘못된 경로로 접근하였을 경우 접근자에게 발생시키는 에러입니다. 자주 보셨을 것입니다.
 
이 페이지를 찾을 수 없습니다.
찾고 있는 페이지가 삭제되었거나 이름이 변경되었거나 일시적으로 사용할 수 없습니다.
 
--------------------------------------------------------------------------------
 
다음을 시도해 보십시오.
 
주소 표시줄에 페이지 주소를 입력했다면 주소를 정확히 입력했는지 확인해 보십시오.
 
nbloger.com 홈 페이지를 연 다음 원하는 정보에 대한 링크를 찾으십시오.
뒤로 단추를 클릭하여 다른 링크를 시도해 보십시오.
HTTP 404 - 파일을 찾을 수 없음
Internet Information Services
 
위에 나타나는 404 에러 페이지는 실제 서버상에 존재하는 html 페이지입니다. 예컨데 접근자가 http://nbloger.com/iendev 형태로 접근하였을 경우 사실 nblog.com 서버상에는 iendev 라는 폴더가 존재하지 않습니다. 따라서 접근자에게 위와 같은 404 에러를 발생시킬 것입니다. 따라서 우리는 위의 404 에러페이지를 바꿀 필요가 있습니다.
IIS에서 웹사이트 등록정보 창의 사용자정의 오류 라는 탭을 보시면 웹사이트 접근에 대하여 발생할 수 있는 에러들에 대하여 확인하실수 있습니다. 이 창에서 404 에러에 대한 부분을 찾으신후 다음과 같이 편집하여 주십시오.
 
메시지 형식 : URL
URL : user_redirect.asp
 
user_redirect.asp 는 접근자가 404 에러를 발생시킬 경우 실행시킬 asp 파일입니다. 위 예제에서 URL 은 웹사이트 홈디렉토리 최상단을 기준으로 한 경로입니다.
이제는 user_redirect.asp 를 만드셔서 접근자의 접근주소를 해석하여 새로운 경로로 이동시켜 주기만 하면됩니다. 다음 예제를 참고하여 주십시오.
 
user_redirect.asp
<%
' 에러번호를 포함한 접근자가 입력한 주소를 받아온다. (404;해당주소 형태)
original_url= Request.ServerVariables("QUERY_STRING")
' ;를 기준으로 분리한다.
url_str = split(original_url,";")
' 분리된 오른쪽 문자열(접근자가 실제로 브라우저상에 입력한 주소)
array_url = split(url_str(1),"/")
' 다시 분리된 문자열에서 아이디를 추출한후 특정 위치로 이동시킨다.
Response.Redirect("user_blog.asp?blogid=" & array_url(3) )
%>

[펌] 404 Error

프로그래밍/Library 2007. 11. 28. 10:55 Posted by galad

http://blog.naver.com/galad/140033607866

출처 블로그 > もも***
원본 http://blog.naver.com/dmsl01/80033521252
 
404 Error 포워딩하기
 
 
간단한 문제는 아니지만 크게 어려운 문제도 아닙니다.
 
해결 방법만 간단히 말하자면 웹서버의 404 오류 처리 루틴을 이용하여 위 문제를 처리할 수 있습니다.
 
구현 원리는 사용자가 http://www.home.net/user 라는 주소로 접근할때 user 라는 경로가 서버에 없을 경우 404 페이지 오류가 발생합니다. 웹서버는 미리 정의된 404 오류 페이지를 불러서 보여주는데, 이때 내가 원하는 처리를 추가 해주면 됩니다.
 
위 부분을 간단한 구현해보겠습니다.
 
먼저 사이트에 다음 파일을 미리 만듭니다.
위치나 파일명은 원하는 데로 해도 상관 없지만 예를 들기 위해 아래에서는 특정 파일명을 사용하였습니다.
 
/error/404.asp
<%
 response.write Request.ServerVariables("QUERY_STRING")
%>
 
그 다음 아래 순서대로 IIS 등록 정보를 변경합니다.
  1. 인터넷 정보 서비스에서 웹사이트의 속성을 엽니다.
  2. 사용자 지정 오류 탭을 선택하고 404 를 찾아 속성 편집 버튼을 클릭합니다.
  3. 메시지 유형URL 로 변경하고, 입력부 /error/404.asp 를 입력합니다.
  4. 확인을 눌러 속성창을 모두 닫습니다.
이제 아무 주소나 입력하여 위의 웹사이트를 입력합니다. 주의 하실 점은 이미 없는 경로를 입력하셔야만 합니다.
 
가령 http://www.home.net/haan 라고 입력하였다면 아래와 같은 결과를 얻을 수 있습니다.
 
404;http://www.home.net/haan
 
위 결과를 보시면 기술적인 문제가 모두 해결된 것을 아실 수 있을겁니다.
 
이제 추가하셔야 할 작업은, 위 QUERY_STRING 경로에서 haan 부분을 분리하여 DB에 해당 사용자가 있는지 확인합니다. 사용자가 있다면 그 사용자에 해당하는 블로그를 화면에 보여주고, 없을 경우에는 일반적인 404 오류와 유사한 화면을 출력합니다. (해당 페이지가 없다는 식의 메시지를 보여줍니다.)
 
 
 
404Error 이전 주소값 알아내기
 

cyworld.com/모모모 는.. 404페이지를 이하는 방식입니다..  404페이지로 이동한다음에 URL을 파싱해서..
모모모 에 해당하는 홈피링크로 이동하는 방식일 것입니다..
대부분의 블로그가 이런식이지 않을까요?
팁텍에 있을듯한데 안보이네여
404에러메세지를 출력할때 현재에러난 페이지의 위치를 알수있는 방법입니다.
테스트환경 : apache 2.0.x,php 4.3.3
아파치설정 :
httpd.conf 파일설정
ErrorDocument 404/error/404.php
404에러페이지를 사용자화한후 주소줄에 에러페이지를 유지한채
에러난 주소값을 알아내는 방법입니다.
404php 파일작성(PHP는 뭐냐?)
http://<?=$_SERVER["HTTP_HOST"]?><?=$_SERVER["REDIRECT_URL"]?>는 존재하지 않습니다. ^^;;

좀더 생각하면 메인페이지를 유지하면서 내부에 404에러를 띄울수도 있습니다.
 
 
Log4J 적용 사례(따라하기) - 손정호
아래 글은 SKT 소액 결재 개발팀 손정호 님이 작성하신 글임을 알려 드립니다.

참고로 preparedStatement에서 적용한 예 입니다.

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

Log4j Summary

이번 WebChannel 개발시에 적용된 Log4j 환경을 바탕으로 작성한 간단한 summary입니다.

1. 다운로드

다운로드http://logging.apache.org/log4j/docs/download.html
매뉴얼http://logging.apache.org/log4j/docs/documentation.html
API spechttp://logging.apache.org/log4j/docs/api/index.html

2. 구조
Log4j는 크게 3가지 요소로 구성되어 있습니다.
① Logger : logging 메시지를 Appender에 전달합니다.
② Appender : 전달받은 logging 메시지를 원하는 곳으로 보내는 매개체의 역할을 합니다.
   아래 표는 Appender의 종류입니다. API에서 보고 이해가 된 선에서 적었습니다.
ConsoleAppender        로그 메시지를 콘솔에 출력합니다.
DailyRollingFileAppender        로그 메시지를 파일로 저장합니다.
DatePattern 옵션에 따라 원하는 기간마다 로그파일을 갱신합니다.
ExternallyRolledFileAppender        
FileAppender        직접적으로 사용되지 않고 DailyRollingFileAppender와 RollingFileAppender의 superclass로 사용되는듯 합니다.
JDBCAppender        로그 메시지를 DB에 저장합니다. 현재는 완벽하지 않으니 왠만하면 차기 버전에서 사용하라고 하는 것 같습니다.
JMSAppender        로그 메시지를 JMS Topic으로 보냅니다.
NTEventLogAppender        NT 이벤트 로그를 위한 Appender. 윈도우에서만 사용가능합니다.
NullAppender        내부적으로만 사용되는 Appender입니다.
RollingFileAppender        로그 메시지를 파일로 저장합니다. 설정된 size를 초과하면 로그파일이 갱신됩니다.
SMTPAppender        로그 메시지를 지정된 이메일로 발송합니다.
SocketAppender        로그 메시지를 socket을 이용해서 지정된 곳으로 보냅니다.
SocketHubAppender        위와 비슷하게 사용하는듯 합니다.
SyslogAppender        로그 메시지를 원격 syslog deamon으로 보냅니다.
TelnetAppender        로그 메시지를 telnet을 통해 보낸다는 것 같습니다. 원격 모니터링, 특히 servlet의 모니터링에 유용하다고 합니다.
WriterAppender        FileAppender처럼 주로 superclass로서 사용되는듯 합니다.
③ Layout : logging 메시지의 출력 형식을 지정합니다.
        - 아래에서 설명.

3. 로깅레벨
FATAL : 가장 크리티컬한 에러가 발생했을 때 사용합니다.
ERROR : 일반적인 에러가 발생했을 때 사용합니다.
WARN : 에러는 아니지만 주의가 필요할 때 사용합니다.
INFO : 일반적인 정보가 필요할 때 사용합니다.
DEBUG : 일반적인 정보를 상세히 나타낼 때 사용합니다.

로깅레벨의 우선순위는 FATAL이 가장 높고 DEBUG가 가장 낮습니다.
예를 들어 레벨을 WARN으로 설정하면 WARN이상되는 로그(FATAL, ERROR, WARN)만
출력합니다.

4. 환경설정
- Log4j의 환경설정은 직접 코드에서 메서드를 이용하는 방법과 properties 파일을 이용하는 방법, XML파일을 이용하는 방법이 있습니다.
① 코드에서 설정
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!!");

위 코드처럼 설정하시면 됩니다.


② properties 파일로 설정
#---------- file logging ----------
log4j.rootLogger=INFO, rolling
#---------- consol logging -----------
#log4j.rootLogger=INFO, stdout
#---------- file, console logging -----------
#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  at %C{3}.%M(%13F:%L) %3x - %m%n
log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
log4j.appender.rolling.File=/WEB_BACKUP1/pgw_log/webchannel.log
log4j.appender.rolling.Append=true
#---------- every day renew ------------
log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
#---------- every month renew ------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM
#---------- every week renew ------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-ww
#---------- every 12hours renew -------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-dd-a
#---------- every hour renew --------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-dd-HH
#---------- every min renew --------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-dd-HH-mm
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
log4j.appender.rolling.layout.ConversionPattern=[%d] %-5p  at %C{3}.%M(%13F:%L) %3x - %m%n


위 properties 파일은 실제 WebChannel에 적용한 파일입니다.
- log4j.rootLogger=INFO, rolling
        : 로깅레벨을 ‘INFO’로 하고 ‘rolling’이라는 이름의 Appender를 사용한다.
        위 properties파일에는 ConsoleAppender(stdout)와 DailyRollingFileAppender(rolling)가
정의되어 있습니다.
- log4j.rootLogger=INFO, stdout : console에만 출력
- log4j.rootLogger=INFO, stdout, rolling : console과 file 로 출력
위처럼 설정이 가능합니다.
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
        : ConsoleAppender의 이름은 ‘stdout’으로 한다.
- log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
        : DailyRollingFileAppender의 이름은 ‘rollong’으로 한다.
- log4j.appender.rolling.File=/WEB_BACKUP1/pgw_log/webchannel.log
        : 로그파일의 위치와 파일명을 지정한다.
- log4j.appender.rolling.Append=true
        : 서버 restart시에도 파일이 reset되지 않는다.
- log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
        : DatePattern 을 ‘매일갱신’으로 설정. 매일 자정이 지나면
파일명 뒤에 날짜가 붙는다.
        ex) webchannel.log.2005-11-21
- log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
        : layout을 PatternLayout으로 설정.
- log4j.appender.rolling.layout.ConversionPattern=[%d] %-5p  at %C{3}.%M(%13F:%L) %3x - %m%n
        : 로그의 출력 형식을 설정. 아래 설명.

# log4j.appender.rolling.MaxFileSize=500KB
: 파일의 최대size 설정하는 부분인데 서버 기동시 최초에 이 부분의 property를 읽지 못했다는 경고가 자꾸 떠서 삭제 했습니다. 설정하지 않으면 Default로 10MB가 설정된다고 합니다.

#### properties 파일의 변경사항은 server restart시에 적용됩니다. ####
③ XML 파일로 설정
현재 잘 모르니 넘어가겠습니다.-_-


5. 설정 포맷
① DatePattern 설정 포맷
'.'yyyy-MM         매달 첫번째날에 로그파일을 변경합니다
'.'yyyy-ww         매주의 시작시 로그파일을 변경합니다.
'.'yyyy-MM-dd        매일 자정에 로그파일을 변경합니다.
'.'yyyy-MM-dd-a        자정과 정오에 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH        매 시간의 시작마다 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH-mm        매분마다 로그파일을 변경합니다.




② PatternLayout 설정 포맷
%p        debug, info, warn, error, fatal 등의 로깅레벨이 출력된다.
%m        로그내용(코드상에서 설정한 내용)이 출력됩니다.
ex) logger.info("log"); 라고 코딩했다면 ‘log’가 로그 내용임.
%d        로깅 이벤트가 발생한 시간을 기록합니다.
포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}
같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t        로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%%        % 표시를 출력하기 위해 사용한다.
%n        플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다.
%c        카테고리를 표시합니다.
ex) 카테고리가 a.b.c 처럼 되어있다면
%c{2}로 설정하면 b.c 가 출력됩니다.
%C        클래스명을 포시합니다.
ex) 클래스구조가 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)를
출력합니다.

ex) [%d] %-5p  at %C{3}.%M(%13F:%L) %3x - %m%n
 [2005-11-23 10:43:21,560] INFO   at
pgw.database.PGWBoardDAO.selectList(PGWBoardDAO.java:146) -
========== PGWBoardDAO#selectList ==========

포맷의 각 색깔별로 출력되는 실제 예입니다. 포맷 중간에 원하는 단어(at)나
기호(`.` , `-`)등을 넣으면 그대로 출력됩니다.



6. 실제 적용 예

다운받은 log4j.jar파일을 원하는 디렉토리에 복사하고 weblogic의
startWebLogic.cmd내의 classpath에 잡아줍니다. buildpath도 잡아주셔야 합니다.

① PGWBoardDAO.java
//import 해줍니다.
import org.apache.log4j.Logger;
.
//중략/

public class PGWBoardDAO extends PGWDAO {
    //parameter로 받은 이름의 instance를 생성합니다.
    static Logger logger = Logger.getLogger("PGWBoardDAO");
.
//중략/
.
public PGWBean selectList(HashMap hashMap) throws Exception {
.
/중략/
.
//            pstmt = conn.prepareStatement(sql.toString());
//LoggableStatement instance생성. LoggableStatement는 아래에서 설명.
            pstmt = new LoggableStatement(conn, sql.toString());

.
//중략/
.
pstmt.setInt(nIdx++, ((curPage-1)*listSize) + 9);
          pstmt.setInt(nIdx++, (curPage-1)*listSize);
        //주어진 로그내용을 ‘INFO’레벨로 출력합니다.
getQueryString()으로 ‘?’가 실제데이터로 치환된 query를 출력합니다.
            logger.info("\n======== PGWBoardDAO#selectList ========\n "
                        + ((LoggableStatement)pstmt).getQueryString() +
                       "\n========================================\n");


.
//중략/
.

        } catch (SQLException se) {
            System.out.println("PGWBoardDAO.selectList SQLException ====" + se);
           //Exception은 ‘ERROR’레벨로 출력합니다.
            logger.error("\n==== PGWBoardDAO#selectList Exception ====" , se );
            return null;
        } catch (Exception e) {
            System.out.println("PGWBoardDAO.selectList Exception ====" + e);
            logger.error("\n==== PGWBoardDAO#selectList Exception ====" , e );
            return null;
        } finally {
.
//중략/


- 위 코드에서 INFO 레벨의 로그는 주어진 로그를 출력하고,
ERROR 레벨의 로그는 발생한 Exception을 로그로 출력합니다.

로그의 출력메서드는 2가지 형식을 지원합니다.
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)

- Throwble 타입의 변수를 parameter로 받는 메서드를 이용하면 원하는 위치에서
원하는 Exception을 발생시킬 수도 있습니다.

- 위 코드에서 INFO 레벨의 로그는 주어진 내용를 출력하고,
ERROR 레벨의 로그는 발생한 Exception을 로그로 출력합니다.


② LoggableStatement.java
- 이 클래스는 query를 로그로 출력할 때 부가적으로 필요한 클래스로 PreparedStatement의 ‘?’를 실제 데이터로 치환해서 출력하는 기능을 합니다.
이 클래스는 Interface인 PreparedStatement를 구현하는 클래스로 파일이름은 임의로 정하셔도 됩니다.
클래스내에는 PrepareddStatement의 메서드를 오버라이딩한 메서드와 넘어온 데이터를 ArrayList에 넣어주는 메서드, 그리고 query의 ‘?’를 치환해 리턴해주는 메서드를 구현합니다.

//PreparedStatement 와 ArrayList를 import 해줍니다.
//메서드 오버라이딩시에 필요한 클래스도 추가적으로 import 해줍니다.
import java.sql.PreparedStatement;
import java.util.ArrayList;

public class LoggableStatement implements PreparedStatement {

        private ArrayList parameterValues;
 
    private String sqlTemplate;

    private PreparedStatement wrappedStatement;

//connection.prepareStatement(String sql) 대신에 사용할 생성자 입니다.
//PreparedStatement Object를 생성, query를 String에 담고 ArrayList를 생성합니다.
    public LoggableStatement(Connection connection, String sql)
            throws SQLException {
            wrappedStatement = connection.prepareStatement(sql);
            sqlTemplate = sql;
            parameterValues = new ArrayList();
    }

.
//중략/
.



//실제로 필요한 메서드만 오버라이딩 하고, 나머지는 auto generate하시면 됩니다.
//여기서는 query문 실행관련 메서드와 setInt, setString, setDate, setCharacterStream 을 오버라이딩 했습니다.
        public boolean execute() throws java.sql.SQLException {
            return wrappedStatement.execute();
    }

        public boolean execute(String sql) throws java.sql.SQLException {
            return wrappedStatement.execute(sql);
    }

        public int[] executeBatch() throws java.sql.SQLException {
            return wrappedStatement.executeBatch();
    }

        public java.sql.ResultSet executeQuery() throws java.sql.SQLException {
            return wrappedStatement.executeQuery();
    }

        public java.sql.ResultSet executeQuery(String sql)
            throws java.sql.SQLException {
            return wrappedStatement.executeQuery(sql);
    }

        public int executeUpdate() throws java.sql.SQLException {
            return wrappedStatement.executeUpdate();
    }

        public int executeUpdate(String sql) throws java.sql.SQLException {
            return wrappedStatement.executeUpdate(sql);
    }

        public java.sql.Connection getConnection() throws java.sql.SQLException {
            return wrappedStatement.getConnection();
    }

       
public void setCharacterStream(
            int parameterIndex,
            java.io.Reader reader,
            int length)
            throws java.sql.SQLException {
            wrappedStatement.setCharacterStream(parameterIndex, reader, length);
            saveQueryParamValue(parameterIndex, reader);

    }

        public void setDate(int parameterIndex, java.sql.Date x)
            throws java.sql.SQLException {
            wrappedStatement.setDate(parameterIndex, x);
            saveQueryParamValue(parameterIndex, x);
    }

        public void setDate(
            int parameterIndex,
            java.sql.Date x,
            java.util.Calendar cal)
            throws java.sql.SQLException {
            wrappedStatement.setDate(parameterIndex, x, cal);
            saveQueryParamValue(parameterIndex, x);
    }

        public void setInt(int parameterIndex, int x)
                        throws java.sql.SQLException {
                        wrappedStatement.setInt(parameterIndex, x);
            saveQueryParamValue(parameterIndex, new Integer(x));
        }

        public void setString(int parameterIndex, String x)
                        throws java.sql.SQLException {        
                        wrappedStatement.setString(parameterIndex, x);
            saveQueryParamValue(parameterIndex, x);
        }
       
//넘어온 데이터를 ArrayList에 담아주는 메서드입니다.
        private void saveQueryParamValue(int position, Object obj) {
                String strValue;
                if (obj instanceof String || obj instanceof Date) {
                        strValue = "'" + obj + "'";
                } else {
                        if (obj == null) {
                              strValue = "null";
                        } else {
                                strValue = obj.toString();
                        }
                }
                while (position >= parameterValues.size()) {
                parameterValues.add(null);
                }
                parameterValues.set(position, strValue);
        }
       
        //instance생성시 String에 넣어둔 query의 ‘?’를 ArrayList에 담긴 실제 데이터로
//치환해서 리턴해 줍니다.
        public String getQueryString() {
                //여기서 query를 String에도 담아준 이유는 webLogic의 jdk가 1.3 버전으로
//StringBuffer의 indexOf(String str) 메서드를 사용할 수 없었기 때문입니다.
//다른 방법이 있으시면 알려주세요..
                String sql = sqlTemplate;
                StringBuffer query = new StringBuffer(sqlTemplate);
                int idx = 0;
               
                if(!parameterValues.isEmpty())
                {
                        for(int i=1;i < parameterValues.size();i++)
                        {
                                idx = sql.indexOf("?");
                                query.replace(idx, idx+1, (String)parameterValues.get(i));
                                sql = query.toString();
                        }
                        parameterValues = null;
                        return query.toString();
                }
                else
                {
                        parameterValues = null;
                        return query.toString();
                }
        }
}


- 다음은 실제 출력문입니다.

[2005-11-23 13:50:19,030] INFO at pgw.database.listDAO.modify(listDAO.java:543)   -
========== llistDAO#modify#if Customer ==========
UPDATE ACKLIST
   SET NM_USER = 'aaaaaaaaaa',
       NO_SSN = '2222222222222',
       NO_MINHEADER = '222',
       NO_MINNUMBER = '22222222',
       REASON = '22222222222222222444444444444444444444',
       ID_MODIFY = 'pbadmin',
       DT_MODIFY = SYSDATE
WHERE SEQ_NUM = '460'
   AND TYPE = '2'

'프로그래밍 > Library' 카테고리의 다른 글

[펌] 가상주소 구현  (0) 2007.11.28
[펌] 404 Error  (0) 2007.11.28
[펌] JSP에서 원하는 Appender 선택하여 쓰기  (0) 2007.11.28
[펌] WEBLOGIC + LOG4J  (0) 2007.11.28
[펌] Log4J...  (0) 2007.11.28

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


참고로 이글의 원저자는 제가 아니므로 퍼가셔서 사용하실때 신중해주시기 바랍니다



만약 log4j 가 처음이라면 이 카테고리의 다음 포스트를 먼저 필독하세요


- log4j 웹에서 사용하기

- log4j 고급스럽게 사용하기

 

 

 

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 log2005-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 log2005-11-07 13:05:58,031 WARN  [http-8080-Processor4] jsp2 (test_jsp2_appender_jsp.java:48)     - warn


'프로그래밍 > Library' 카테고리의 다른 글

[펌] 404 Error  (0) 2007.11.28
[펌] Log4J 적용 사례(따라하기)  (0) 2007.11.28
[펌] WEBLOGIC + LOG4J  (0) 2007.11.28
[펌] Log4J...  (0) 2007.11.28
[펌] Log4j를 사용하여 Tomcat 5.x로깅 설정하기  (0) 2007.11.28

[펌] WEBLOGIC + LOG4J

프로그래밍/Library 2007. 11. 28. 09:38 Posted by galad
WEBLOGIC + LOG4J
조회(593)
Unix/Linux | 2007/05/11 (금) 12:11
공감하기 | 스크랩하기
<BASE target="_son">
log는 웹로직 7에서 작성되었다.

이전에 쓴 log모두다 weblogic 7을 기준으로 작성되었음을 밝힌다.
웹로직 8이상에서는 weblogic workshop 에서 log4j를 사용하고 있는듯하고
이것으로 충돌이 날수도 있다. 이런경우 bea사이트에서 도움을 받기 바란다.
문서로 적혀있는것을 언젠가 본적이 있는것 같다.
많은 사람들이 opensorce가 주제인 이 log들에 대해 왜 weblogic이냐고 묻는다. JBOSS 혹은 다른것도 몇개 있는데 말이다.
여기 있는 글중의 일부는 현업에서 충분히 쓰이고 있는 글을 다루다 보니
Weblogic 을 기준으로 적었지만 앞으로 open source WAS를 조금씩 바꿔가는 연습을 할것이다.

log4j에 관한 log를 상당히 오랫만에 적는다.
아무도 묻지도 않았기도 했지만...그동안 정신이 딴데 팔려서...


ps : 하지만 모든 WAS및 Tomcat 등의 servlet container에서도 잘 될듯하다. 아래의 원리를 안다면 말이다.



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

웹로직에서의 log4j를 쓰는 방법에 대해서 많이 생각을 해봤다.
좀처럼 쉽지 않았음 T.T
원리는 간단한데 말이다.
일단 log4j의 특성을 알아야 한다.
이 log를 적기 위해 별별 opensoure를 뒤져야 했다. 몇몇 프레임웍및 opensource 혹은 상용소스들을 decompile해서 소스를 뒤졌다.
log4j를 이렇게 많이 쓰면서...
이렇게 자료가 없단말인가?

때문에 보다 객관적이고 정확한 방법으로 log를 쓰고 싶었다.
다른 분들도 보고 있기 때문에....
언제나 log를 쓰면서 느끼는건데 부끄럽다.



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

그냥 T.T
Jakarta Commons Logging은 java.util.logging 과의 연결 또는 Jakarta Log4j에 사용될 수 있다. Commons Logging은 로깅 추상 레이어로서 애플리케이션을 기저의 로깅 구현에서 고립시킬 수 있다. Commons Logging을 사용하여 설정 파일을 변경하여 기저의 로깅 구현을 바꿀 수 있다. Commons Logging은 Jakarta Struts 1.1과 Jakarta HttpClient 2.0에 사용된다.







2. 환경설정

1. C:beaweblogic700commonlib 아래에 log4j.jar 를 복사해 넣는다.
2. C:beauser_projectsmydomain 에서 startWeblogic.cmd를 열고
set JAVA_OPTIONS=-Dlog4j.config=log4j.xml -Dweblogic.security.SSL.trustedCAKeyStore=C:bea2weblogic700serverlibcacerts 와 같이 JAVA_OPTIONS을 수정한다.
물론 xml configuration및 properties파일은 startWeblogic.cmd가 있는 곳에 둬야 한다.

3. 이로서 환경 설정은 끝이다.
참고 : 만일 log4j.config파일을 사용하려면 위의 2의 Dlog4j.config=log4j.xml 을 Dlog4j.config=log4j.config 로 수정만 하면 된다.
이 예제에서는 xml를 사용하기로 했으니..xml예제를 한개 올린다.

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

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

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

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

</log4j:configuration>




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

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

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

import org.apache.log4j.Logger;

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

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

}



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

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

</web-app>



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

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

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

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

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

  public void init() {

    int debug = 0;
    String value;
    String logFile;

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

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

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

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


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


5. log확인 T.T

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



6. 잡소리 T.T
LOG4J의 Weblogic에서의 설정을 마치고 사용법도 익혔다.
만일 tomcat및 다른 container에서 사용하려면 class path(tomcat의 경우는 xxx/yyy/lib 인데 기억안나지만 그곳에 log4j.jar를 넣어두면 된다.
그리고 web.xml및 Log4jInit.class 을 적절한 위치에 넣어두면 된다.
아마 다 될것이라고 믿는다.

[펌] Log4J...

프로그래밍/Library 2007. 11. 28. 09:38 Posted by galad

/**
 * Created on 2004. 3. 05.
 *
 *
 * @author 짱가
 *
 * *******************************************************
 *                             코드수정히스토리
 * 날짜                               작업자                      내용
 * 2004. 3. 3.                       짱가
 * *******************************************************
 *
 */
/**
 * System.out.println("[WARN]  : " +  e.toSting()  );
 * 이런식으로 코딩해본적 있을것이다. 혹시라도 로그를 빼달라고 하면 모두들
 * System.out.println("[WARN] : " +  e.toSting()  ); //라고 해본사람들이 꽤될것이다.
 * 컴파일 했다가 말았다가 어느게 시스템 Debug용이고 어느게 뭔지 .....
 * 프로그래밍이 끝나고 운용에 들어가면서 속도 향상을 위해 클라이언트가 FATAL에러만 빼고 모두 빼달라고 했다던지
 * 혹은 운용중에 에러를 잡기위해 어느단계까지는 모두 나오게 해야한다던지 할때 이런기능이 없다면
 * 아마 소스코드는 IF문과 System.out.println() 으로 뒤범벅이 될것이다.
 *
 *
 * 로그레벨이라는 개념을 사용하면 약간 수고를 덜수 있다.
 * DEBUG < INFO < WARN < ERROR < FATAL
 * 보통 로그level에서 DEBUG가 가장작고 FATAL이 가장크다. 그래서 위의 예제 결과는 아래와 같이 나온다.
 * 결과를 봐서 알겠지만 예제에서 WORN을 LogLevel로 삼았기 때문에 위의 그림과 같게 아래의 결과가 나온다.
 * java.lang.Object
 *|
 * +--org.apache.log4j.Category
 *       |
 *       +--org.apache.log4j.Logger
 * 보는 바와 같이 Logger class는 Category의 child class였다.
 *
 *
 *
 *
 * API의 일부....
 *
 * Constructor Summary
 * protected Logger(String name)
 *
 * Method Summary
 *
 * 1) static Logger getLogger(Class clazz) --- Same as calling getLogger(clazz.getName()).
 *
 * 2) static Logger getLogger(String name) --- Retrieve a logger by name.
 *
 * 3) static Logger getLogger(String name, LoggerFactory factory) ---Like getLogger(String)
 *     except that the type of logger instantiated depends on the type
 *     returned by the LoggerFactory.makeNewLoggerInstance(java.lang.String) method of the factory parameter.
 *
 * 4) static Logger getRootLogger() --- Retrieve the root logger. 
 *
 * ----------------------------------------------------------------------------------------------------
 * 1)과 2번은 클라스로 근본적으로 같고 예를들면 Logger.getLogger( xxxx.class.getName( ) )
 * 이런식으로 쓰므로 1번과 2번은 근본적으로 같다고 볼수 있다.
 * 3)은 LoggerFactory에 의해 불리우는것에 따라 logger type이 달라지고....
 * 4)째는 모든 Logger는 부모가 있는데 이 부모logger를 사용가능하지만 별로 권장하지 않는다고 한다.
 * http://www.onjava.com/lpt/a/2525 을 참조하기 바란다.
 * 이글에 보면 Tomcat에서의 사용법까지 나와있으며 어떻게 로그를 남기는것이 효율적인지에 대하여 적고 있다.
 *--------------------------------------------------------------------------------------
 *
 * logger class는 Category 클라스의 setLevel( )을 호출하게 되면 level이 정해진다.
 * http://logging.apache.org/log4j/docs/api/org/apache/log4j/Level.html 에서 보는바와같이 level의 종류는
 * 코드:
 * static Level ALL
 * static Level DEBUG
 * static Level ERROR
 * static Level FATAL
 * static Level INFO
 * static Level OFF
 * static Level WARN 와같다.
 * http://logging.apache.org/log4j/docs/api/org/apache/log4j/Category.html
 * 실제 로그를 뿌리는 메서드들을 발견할수 있을것이다. 
 *

 * Log4j 는 기본적으로 다섯개의 우선권(이하 Priority) 등급으로 메세지를 로깅할 수 있다.
 * 1. 완성된 어플리케이션에서는 출력되지 않아야 할 디버깅 메세지들을 쓰기위해 debug 를 사용하라.
 * 2. 어플리케이션의 verbose 모드에서 출력될만한 메세지들을 로깅하기 위해 info 를 사용하라.
 * 3. 어플리케이션이 이상없이 계속 실행될 수 있는 정도의 경고메세지를 로깅하기 위해 warn 을 사용하라.
 * 4. 어플리케이션이 그럭저럭 돌아갈만한 정도의 에러베세지를 로깅하기 위해 error 를 사용하라.
   예를들어 관리자에 의해 주어진 설정인자가 올바르지 않아 하드코딩된 기본값을 사용해야 할 경우.
 * 5. 로깅후에 애플리케이션이 비정상적으로 종료될 치명적인 메세지를 로깅하기 위해 fatal 을 사용하라.

 *****************************************************************************
 *
 *  여기서 작성한 클래스는 각 기능 별로 txt로 생성하는 로그
 *                                          html로 생성하는 로그
 *           pattern matching을 사용하는 로그
 *            xml로 생성하는 로그
 *           그리고 configuration file로 xml을 사용하여 log파일로 생성하고
 *           그 log파일이 일정 사이즈가 될때 증가시키는 로그로 나뉘어져 있다
 *  참고로 각 실행시에 main으로 실행한후 메소드 명만 수정하였으므로 실행은 그렇게 하면 실행이 가능하다.
 *
 *****************************************************************************
 *
 */

import java.io.FileOutputStream;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.HTMLLayout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.WriterAppender;
import org.apache.log4j.xml.DOMConfigurator;
import org.apache.log4j.xml.XMLLayout;

public class Logging4J {

 //  Logger.getInstance()의 실체는 Category.getInstance()이며,
 //  Category가 반환되기때문이다. 그리고 이 Category 클래스나 getInstance()
 //     메소드는 추천 되지 않는 API인것으로 알려져있다.
 //  Logger의 취득은 Logger.getLogger()로 하기 바란다
 static Logger logger = Logger.getLogger(Logging4J.class);

 public void consoleLog() {

  Logger logger = Logger.getLogger("loggerTest");
  BasicConfigurator.configure();
  logger.setLevel(Level.WARN);
  logger.debug("this is debug!!!");
  logger.info("this is info!!!");
  logger.warn("this is warn!!!");
  logger.error("this is error!!!");
  logger.fatal("this is fatal!!!");

 }
 /**
  * 생성 로그  txt
  * DEBUG - debug!!!
  * INFO - info!!!
  * WARN - warn!!!
  * ERROR - error!!!
  * FATAL - fatal!!!     *
  */
 public void txtLog() {

  SimpleLayout layout = new SimpleLayout();

  FileAppender appender = null;
  try {
   appender = new FileAppender(layout, "Logging4J.txt", false);
  } catch (Exception e) {
  }

  //  FileAppender(Layout layout, String filename)  : constructor
  //  FileAppender(Layout layout, String filename, boolean append)

  logger.addAppender(appender);

  logger.setLevel(Level.DEBUG);
  logger.debug("debug!!!");
  logger.info("info!!!");
  logger.warn("warn!!!");
  logger.error("error!!!");
  logger.fatal("fatal!!!");
 }
 // html은 너무 길다.. ^^;;
 public void htmlLog() {

  HTMLLayout layout = new HTMLLayout();

  WriterAppender appender = null;
  try {
   FileOutputStream output = new FileOutputStream("Logging4J.html");
   appender = new WriterAppender(layout, output);
  } catch (Exception e) {
  }

  //  FileAppender(Layout layout, String filename)  : constructor
  //  FileAppender(Layout layout, String filename, boolean append)
  //  Logger.getInstance()의 실체는 Category.getInstance()이며,
  //  Category가 반환되기때문이다. 그리고 이 Category 클래스나 getInstance() 메소드는
  //     추천 되지 않는 API인것으로 알려져있다.
  //  Logger의 취득은 Logger.getLogger()로 하기 바란다

  logger.addAppender(appender);

  logger.setLevel(Level.DEBUG);
  logger.debug("HERE is some Debug!!!");
  logger.info("HERE is some info!!!");
  logger.warn("HERE is some WARN!!!");
  logger.error("HERE is some ERROR!!!");
  logger.fatal("HERE is some FATAL!!!");
 }
 /**
  * Classname: Logging4J
  * Date in ISO8601 format: 2004-03-04 11:08:43,950
  * 이벤트 위치: Logging4J.main(Logging4J.java:194)
  * 메시지 : Here is some DEBUG
  * 
  * Classname: Logging4J
  * Date in ISO8601 format: 2004-03-04 11:08:43,950
  * 이벤트 위치: Logging4J.main(Logging4J.java:195)
  * 메시지 : Here is some INFO
  * 
  * Classname: Logging4J
  * Date in ISO8601 format: 2004-03-04 11:08:43,950
  * 이벤트 위치: Logging4J.main(Logging4J.java:196)
  * 메시지 : Here is some WARN
  * 
  * Classname: Logging4J
  * Date in ISO8601 format: 2004-03-04 11:08:43,950
  * 이벤트 위치: Logging4J.main(Logging4J.java:197)
  * 메시지 : Here is some ERROR
  * 
  * Classname: Logging4J
  * Date in ISO8601 format: 2004-03-04 11:08:43,950
  * 이벤트 위치: Logging4J.main(Logging4J.java:198)
  * 메시지 : Here is some FATAL
  *
  *
  */
 public void patternLog() {
  // Note, %n is newline
  String pattern = "Classname: %C %n";
  pattern += "Date in ISO8601 format: %d{ISO8601} %n";
  pattern += "이벤트 위치: %l %n";
  pattern += "메시지 : %m %n %n";

  PatternLayout layout = new PatternLayout(pattern);
  ConsoleAppender appender = new ConsoleAppender(layout);

  logger.addAppender(appender);
  logger.setLevel((Level) Level.DEBUG);

  logger.debug("Here is some DEBUG");
  logger.info("Here is some INFO");
  logger.warn("Here is some WARN");
  logger.error("Here is some ERROR");
  logger.fatal("Here is some FATAL");
 }
 // xml로 기록
 public void xmlLog() {
  XMLLayout layout = new XMLLayout();

  //option setting
  layout.setLocationInfo(true);

  FileAppender appender = null;
  try {
   appender = new FileAppender(layout, "Logging4J.xml", false);
  } catch (Exception e) {
  }

  logger.addAppender(appender);
  logger.setLevel((Level) Level.DEBUG);

  logger.debug("Here is some DEBUG");
  logger.info("Here is some INFO");
  logger.warn("Here is some WARN");
  logger.error("Here is some ERROR");
  logger.fatal("Here is some FATAL");
 }

 //--------참고.    http://www.vipan.com/htdocs/log4jhelp.html  --------//
 // for using xml conf

 // properties를 이용하는 것은 다루지 않았다.

 /**
  * conf file
  * <?xml version="1.0" encoding="UTF-8" ?>
  * <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
  *
  * <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  *
  *    <appender name="cad" class="org.apache.log4j.FileAppender">
  *       <param name="File"   value="Logging4J.xml" />
  *       <param name="Append" value="false" />
  *       <layout class="org.apache.log4j.xml.XMLLayout"/>
  *    </appender>
  *
  *    <root>
  *       <priority value ="debug" />
  *       <appender-ref ref="cad"/>
  *    </root>
  *
  * </log4j:configuration>
  *
  */
 public void confLog() {
  //dom conf start
  String conf = "log4jconf.xml";
  DOMConfigurator.configure(conf);
  //dom conf end

  logger.debug("Here is some DEBUG");
  logger.info("Here is some INFO");
  logger.warn("Here is some WARN");
  logger.error("Here is some ERROR");
  logger.fatal("Here is some FATAL");
 }
 //LollingFileAppender
 //log4j가 쌓는 로그의 순서
 //최근것은 항상 Logging4J.log 에 쌓이고 이것이 예에서 1000바이트가 차들어가면
 //Logging4J.log를 복사하여
 //Logging4J.log.1을 만들고 Logging4J.log에는 최근게 계속 쌓인다.
 /**
  * <?xml version="1.0" encoding="UTF-8" ?>
  * <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
  *
  * <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  *
  *    <appender name="cad" class="org.apache.log4j.RollingFileAppender">
  *       <!--<param name="File"   value="Logging4J.xml" />-->
  *       <param name="File"   value="Logging4J.log" />
  *       <param name="Append" value="true" />
  *       <param name="MaxFileSize" value="1000"/>
  *       <param name="MaxBackupIndex" value="3"/>
  *
  *
  *       <!--<layout class="org.apache.log4j.xml.XMLLayout">
  *       </layout>-->
  *
  *       <layout class="org.apache.log4j.PatternLayout">
  *          <param name="ConversionPattern"
  *                 value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
  *       </layout>
  *
  *    </appender>
  *
  *    <root>
  *       <priority value ="debug" />
  *       <appender-ref ref="cad"/>
  *    </root>
  *
  * </log4j:configuration>
  */
 public void lollingLog() {
  //dom conf start
  String conf = "log4jconfLolling.xml";
  DOMConfigurator.configure(conf);
  //dom conf end

  logger.debug("Here is some DEBUG");
  logger.info("Here is some INFO");
  logger.warn("Here is some WARN");
  logger.error("Here is some ERROR");
  logger.fatal("Here is some FATAL");
 }

 // pattern layout
 // --참고 -- http://logging.apache.org/log4j/docs/api/org/apache/log4j/PatternLayout.html
 /**
  * 출력패턴
  *
  * * ------+-------------------------------------------------------------------------------
 * %c    | 로그 이벤트의 카테고리명을 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %C    | 로깅 요구를 실시하는 클래스명을 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %d    | 로그 이벤트의 일시를 출력한다.
 *       | %d{HH:mm:ss} 나 %d{dd MMM yyyy HH}로서보다 유연하게 일시 정보를 출력할 수가 있다. 
 * ------|-------------------------------------------------------------------------------
 * %F(*) | 로그 요구가 발생한 파일명을 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %l(*) | 로그가 생성되었을 때에 불려 간 위치(소스명, 행)를 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %L(*) | 로깅 요구를 행한 행 번호를 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %m    | 로깅이벤트로 설정된 메세지를 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %M(*) | 로그 요구가 행해진 메소드명을 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %n    | 플랫폼 의존의 개행 문자를 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %p    | 로그의 우선도를 출력합니다. 
 * ------|-------------------------------------------------------------------------------
 * %r    | 어플리케이션이 개시하고 나서, 로그가 출력될 때까지의 시간을 밀리 세컨드 단위로 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %t    | 로그를 생성한 thread의 이름을 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %x    | 로그가 생성된 thread의 NDC(네스트화 진단 문맥)를 출력한다. 
 * ------|-------------------------------------------------------------------------------
 * %%    | %를 출력한다. 
 * ------+-------------------------------------------------------------------------------
 *
 *(*)이것들을 출력할 때의 퍼포먼스는 좋지 않기 때문에, 어플리케이션의 실행 속도가 문제가 되지 않는 경우에게만
 *사용하는 것이 추천 되고 있다
 *
 *
 ** 여러가지 출력포멧 : 위의 xml에 아래의 포멧들을 적용해서 이것저것 찍어보도록 하자코드:
 * ----------+-------------------------------------------------------------------------
 * %10m      | 출력 캐릭터 라인이 10 문자 이하의 경우,
 *           | 캐릭터 라인의 좌측으로 공백이 삽입된다. 
 * ----------+-------------------------------------------------------------------------
 * %-10m     | 출력 캐릭터 라인이 10 문자 이하의 경우,
 *           | 캐릭터 라인의 우측으로 공백이 삽입된다. 
 * ----------+-------------------------------------------------------------------------
 * %.10m     | 출력 캐릭터 라인이 10 문자를 넘는 경우,
 *           | 캐릭터 라인의 오른쪽으로부터 세어 11 문자눈 이후의 문자(선두의 문자)가 잘라내진다. 
 * ----------+-------------------------------------------------------------------------
 * %10.20m   | 출력 캐릭터 라인이 10 문자 이하의 경우,
 *           | 캐릭터 라인의 좌측으로 공백이 삽입된다.
 *           | 출력 캐릭터 라인이 20 문자를 넘는 경우,
 *           | 캐릭터 라인의 오른쪽으로부터 세어
 *           | 21 문자 이후의 문자(선두의 문자)가 잘라내진다. 
 * ----------+-------------------------------------------------------------------------
 * %-10.20m  | 출력 캐릭터 라인이 10 문자 이하의 경우,
 *           | 캐릭터 라인의 우측으로 공백이 삽입된다.
 *           | 출력 캐릭터 라인이 20 문자를 넘는 경우,
 *           | 캐릭터 라인의 오른쪽으로부터 세어 21 문자이후의 문자(선두의 문자)가 잘라내진다. 
 * ----------+-------------------------------------------------------------------------
 *
 *
 *  패턴 layout을 쓰는 예제 xml conf 파일코드:
 * <?xml version="1.0" encoding="UTF-8" ?>
 * <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 *
 * <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 *
 *    <appender name="cad" class="org.apache.log4j.RollingFileAppender">
 *       <!--<param name="File"   value="Logging4J.xml" />-->
 *       <param name="File"   value="Logging4J.log" />
 *       <param name="Append" value="true" />       
 *       <param name="MaxFileSize" value="1000"/>
 *       <param name="MaxBackupIndex" value="3"/>
 *      
 *      
 *       <!--<layout class="org.apache.log4j.xml.XMLLayout">
 *       </layout>-->
 *      
 *       <layout class="org.apache.log4j.PatternLayout">
 *          <param name="ConversionPattern"
 *                 value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
 *       </layout>           
 *       
 *    </appender>
 *
 *    <root>
 *       <priority value ="debug" />
 *       <appender-ref ref="cad"/>
 *    </root>
 *
 * </log4j:configuration> 
  */
 
 public static  void main(String[] args) {
   //dom conf start
   String conf = "log4jPattern.xml";
   DOMConfigurator.configure(conf);
   //dom conf end

   logger.debug("Here is some DEBUG");
   logger.info("Here is some INFO");
   logger.warn("Here is some WARN");
   logger.error("Here is some ERROR");
   logger.fatal("Here is some FATAL");
  }
/**
 *  결과
 * 2004-03-04 11:27:57,038 DEBUG [main] Logging4J (Logging4J.java:440) - Here is some DEBUG
 * 2004-03-04 11:27:57,038 INFO  [main] Logging4J (Logging4J.java:441) - Here is some INFO
 * 2004-03-04 11:27:57,038 WARN  [main] Logging4J (Logging4J.java:442) - Here is some WARN
 * 2004-03-04 11:27:57,038 ERROR [main] Logging4J (Logging4J.java:443) - Here is some ERROR
 * 2004-03-04 11:27:57,038 FATAL [main] Logging4J (Logging4J.java:444) - Here is some FATAL
 */
 
}

Log4j를 사용하여 Tomcat 5.x로깅 설정하기

알림 : 여기의 방법은 Tomcat에 log4j로깅을 셋팅하기 위한것이다. 당신의 애플리케이션은 해당 웹 애플리케이션만의 log4j.properties를 WEB-INF/classes디렉토리로 배치될수 있다.

$CATALINA_HOME/common/classes에 log4j.properties파일을 생성한다.

log4j.rootLogger=ERROR, R
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=${catalina.home}/logs/tomcat.log
log4j.appender.R.DatePattern='.'yyyy-MM-dd
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern={%p} %c{2} %x %m%n

commons-logging.jar 와 log4j-1.2.9.jar을 $CATALINA_HOME/common/lib로 복사할 필요가 있다. 주의, 몇몇 참조문서에서는 $CATALINA_HOME/server/lib로 이 파일을 복사해야 한다고 말하고 있으나 나의 경우 common/lib로 복사하는 것이 잘 작동했다.

그리고 나서, 당신의 애플리케이션 log4j.properties($APPFUSE_HOME/web/WEB-INF/classes)에 다음의 내용을 추가한다.

# Suppress the tomcat logging whilst DEBUG is switched on
log4j.logger.org.apache.catalina.core=ERROR
log4j.logger.org.apache.catalina.session=ERROR
log4j.logger.org.apache.jasper.compiler=ERROR

개발하는 동안, 당신은 root logger를 DEBUG로 셋팅한다. 그리고 나서 Tomcat로깅이 DEBUG로 셋팅이 되면, 당신은 Jasper컴파일러로부터 수천라인의 컴파일 추적메시지를 보게될것이다.

물론, 특정 애플리케이션을 구별하여, 당신의 애플리케이션 log4j.properties ROOT로그 레벨을 WARN이나 ERROR로 유지하고 특정 애플리케이션을 위해 디버깅을 활성화한다.(이를테면, org.appfuse=DEBUG)

노트: 만약 당신이 stdout으로 Tomcat로깅을 수행하길 원한다면, 당신의 $CATALINA_HOME/common/classes/log4j.properties파일은 다음과 같을것이다.

log4j.rootLogger=ERROR, stdout, TOMCAT

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{dd-MM-yy HH:mm:ss:SSS} - {%p} %c{2} Thread [%t]; %x %m%n

log4j.appender.TOMCAT=org.apache.log4j.DailyRollingFileAppender
log4j.appender.TOMCAT.File=${catalina.home}/logs/tomcat.log
log4j.appender.TOMCAT.DatePattern='.'yyyy-MM-dd
log4j.appender.TOMCAT.layout=org.apache.log4j.PatternLayout
log4j.appender.TOMCAT.layout.ConversionPattern=%d{dd-MM-yy HH:mm:ss:SSS} - {%p} %c{2} Thread [%t]; %x %m%n

'프로그래밍 > Library' 카테고리의 다른 글

[펌] WEBLOGIC + LOG4J  (0) 2007.11.28
[펌] Log4J...  (0) 2007.11.28
[펌] log4j 설정법  (0) 2007.11.28
[펌] Log4J Configuration  (0) 2007.11.28
[펌] Log4J 사용하기  (0) 2007.11.28