[자랑질]Arare 국제인증(?) 획득 !!!!!

Cross DBMS Query Executer인 Arare가 국제인증(?)을  획득하였습니다. ^^

오늘 Gmail을 통해 받은 메일 본문입니다.

Congratulations,

Arare SQL Executer, one of your products, has been added to Softpedia's
database of software programs for Mac OS. It is featured with a description
text, screenshots, download links and technical details on this page:
http://mac.softpedia.com/get/Developer-Tools/Arare-SQL-Executer.shtml

The description text was created by our editors, using sources such as text
from your product's homepage, information from its help system, the PAD
file (if available) and the editor's own opinions on the program itself.


"Arare SQL Executer" has been tested in the Softpedia labs using several
industry-leading security solutions and found to be completely clean of
adware/spyware components. We are impressed with the quality of your
product and encourage you to keep these high standards in the future.

To assure our visitors that Arare SQL Executer is clean, we have granted it
with the "100% FREE" Softpedia award. To let your users know about this
certification, you may display this award on your website, on software
boxes or inside your product.

More information about your product's certification and the award is
available on this page:
http://mac.softpedia.com/progClean/Arare-SQL-Executer-Clean-110839.html

Feel free to link to us using the URLs above. If you choose to link to the
clean award page for your product, you may use the award graphic or a text
link: "100% FREE award granted by Softpedia".


If you feel that having your product listed on Softpedia is not a benefit
for you or simply need something changed or updated, please contact us via
email at webmaster@softpedia.com and we will work with you to fix any
problem you may have found with the product's listing.

특별히 신청한 것도 없었는데 지들이 알아서 검토하고 평가해서 등록시켰더군요...ㅋㅋㅋ

다만, 아쉬운 것은 100% pure java로 만들어진 Application이라 Windows는 물론, Linux에서도 구동 가능한데 Mac용으로 소개해놓았더라구요....ㅡ.ㅡ

당근, 항의메일 보냈습니다.(근데 짧은 영어로 보낸거라 이해하려나 모르겠네요.....쩝)

Softpedia에 등록된 화면도 자랑질 하렵니다. ^^

http://mac.softpedia.com/get/Developer-Tools/Arare-SQL-Executer.shtml

by 제이 | 2012/01/06 15:04 | 트랙백 | 덧글(0)
Arare release (v0.9_r28)

꽤나 오랫동안 끄적거려오던 작업을 baseline을 그으려 한다.일단, 시작은 Oracle작업 시 사용하던 Golden의 기능을 Tibero나 MySql 등의 다양한 DB에서 사용하고자 만들게 되었다.

JDBC라는 뛰어난(?) 개념때문에 쉽게 접근가능하리라 생각했으나, 천성이 게을러서 빠른 진도를 못 나갔던 것이 사실이다.

프로젝트는 Google Code에서 진행하고 있었고,

당연히, Member는 혼자이다..ㅋㅋ

100% pure java이며, pure Swing이다.

Swing을 다루기 좋은 Netbeans로 작업하였다.

지금까지 작업한 기준으로 Binary와 Source를 Open하고자 한다.

물론, GNU 라이선스를 따름은 당연하다.

code사이트는 http://code.google.com/p/arare/ 이다. member로 참여코자 하시는 분 있으시면 신청해 주시길 바란다.
이글루스의 용량문제로 직접 첨부하지는 못하고 링크로 대신한다.
http://arare.googlecode.com/files/arare.zip

위의 download 링크에서 다운받으신 zip파일을 여시고 첨부된 bat파일을 실행하시면 된다.

많은 사용과 Feedback부탁드린다.

by 제이 | 2011/09/14 20:03 | Arare | 트랙백 | 덧글(0)
Arare
Arare
by 제이 | 2009/02/12 13:49 | Arare | 트랙백 | 덧글(0)
Radix Sort
public int[] doSort (int[] aArr) {
    int i, j, cipher, num, max; 
    int [][] bucket = new int [10][aArr.length];
    int [] count = new int [10];
        
    max = 0;
        
    for(i=0;i<aArr.length;i++){
        if(max<aArr[i])
        max = aArr[i];
    }
        
    cipher = 1;
        
    while(cipher<=max){
        for(i=0;i<=9;i++)
            count[i] = 0;
        for(i=0;i<aArr.length;i++){
            num = (aArr[i]/cipher)%10;
            bucket[num][count[num]++] = aArr[i];
        }
        num = 0;
        for(i=0;i<=9;i++){
            for(j=0;j<count[i];j++){
                aArr[num++] = bucket[i][j];
            }
        }
        cipher *= 10;
    }        
    return aArr;
}
by 제이 | 2007/09/06 09:37 | 기초다 기초...쩝 | 트랙백 | 덧글(0)
Quick Sort

public int[] doSort(int[] aList, int aLow, int aHigh){
    int lo = aLow;
    int hi = aHigh;
    if (lo >= hi){
        return aList;
    } else if (lo == hi - 1) {
        if(aList[lo] > aList[hi]){
            int T = aList[lo];
            aList[lo] = aList[hi];
            aList[hi] = T;
        }
        return aList;
    }

    int pivot = aList[(lo + hi) / 2];
    aList[(lo + hi) / 2] = aList[hi];
    aList[hi] = pivot;
     
    while (lo < hi){
        while (aList[lo] <= pivot && lo < hi){
            lo++;
        }
        while (pivot <= aList[hi] && lo < hi){
            hi--;
        }
        if (lo < hi){
            int T = aList[lo];
            aList[lo] = aList[hi];
            aList[hi] = T;
        }
    }
     
    aList[aHigh] = aList[hi];
    aList[hi] = pivot;

    doSort(aList, aLow, lo - 1);
    doSort(aList, hi + 1, aHigh);
    return aList;
}

by 제이 | 2007/09/06 09:36 | 기초다 기초...쩝 | 트랙백 | 덧글(0)
Shell Sort
public int[] doSort(int[] aList){
    int h,i,j,k,temp,n = 0;
    n = aList.length;
    for(h=n/2;h>0;h=h/2){
        for(i=0;i<h;i++){
            for(j=i+h;j<n;j=j+h){
                temp = aList[j];
                for(k=j;k>h-1&&aList[k-h]>temp;k-=h)
                    aList[k] = aList[k-h];
                aList[k] = temp;
            }
        }
    }
    return aList;
}
by 제이 | 2007/09/06 09:34 | 기초다 기초...쩝 | 트랙백 | 덧글(0)
Bubble Sort
public int[] doSort(int[] aList){
    int i, j, temp;
    for(i=0;i<aList.length-1;i++){
        for(j=1;j<aList.length-i;j++){
            if(aList[j-1] > aList[j]){
                temp = aList[j-1];
                aList[j-1] = aList[j];
                aList[j] = temp;
            }
        }
    }
    return aList;
}
by 제이 | 2007/09/06 09:32 | 기초다 기초...쩝 | 트랙백 | 덧글(0)
Selection Sort
public int[] doSort(int[] aList){
    int k,j,i,temp = 0;
    for(i=0;i<aList.length-1;i++){
        k = i;
        for(j=i+1;j<aList.length;j++){
            if(aList[k]>aList[j]){
                k=j;
            }
        }
        temp = aList[i];
        aList[i] = aList[k];
        aList[k] = temp;
    }
    return aList;
}
by 제이 | 2007/09/06 09:31 | 기초다 기초...쩝 | 트랙백 | 덧글(0)
Insertion Sort
public int[] doSort(int[] aList){
    int k,j = 0;
    for(int i=1;i<aList.length;i++){
    k = aList[i];
    for(j=i-1;j>=0 && k < aList[j];j--)
        aList[j+1] = aList[j];
        aList[j+1] = k;
    }
    return aList;
}
by 제이 | 2007/09/06 09:29 | 기초다 기초...쩝 | 트랙백 | 덧글(0)
J2EE 어플리케이션의 battleneck 발생과 대처방법
회사를 옮기고 맡은 업무가 TA&SA인데...그 중 특히나 J2EE appl에 대한 대처다....

이글 저글 읽다가...발견한 글인데 .... 최고의 글이다.......감동감동감동 !!!!!

여기 오는 독자들은 꼭 읽어보길...권한다...강추한다.

http://cafe.naver.com/ArticleRead.nhn?clubid=12263288&menuid=3&boardtype=&page=3&articleid=14
by 제이 | 2007/08/24 17:52 | JAVA, JSP 썅!! | 트랙백 | 덧글(2)
Ajax로 맹그는 Web Tail(2)

꽤나 오랫동안 연재를 하지 않았기에 혹시나 있지 모를 독자들에게 죄송하단 말을 하고 싶다.

하지만, 아마도 아무도 기다리지 않았으리란 생각이 앞서긴 한다...^^

일단, 지난 연재 때는 요구사항을 아주 가볍게 분석해 봤다.

물론, 이따위의 요구사항 분석법으로 프로젝트를 진행한건 절대 아님을 알려둔다...

다만, 이 글에서는 여러분이나 나나 편하게 이야기하는 형식을 따르고 있으므로 서로 쉽게 받아들이도록 했음을 이해하기 바란다.

각설하고,

분석결과에 따라 구현되는 시스템은 아래와 같다.

뭐 간단해 보인다...

맞다 간단하다...^^

이제 코드를 알아보자.

앞서 언급한 바와 같이 Server측과 Applet측은 이미 구현되어 있는 것을 그대로 사용하므로 여기서 새롭게 언급하진 않겠다.

그럼 Ajax관련 코드만 나열하면 아래와 같다.

1. LogTailServlet.java
2. LogViewer.html

아주 간단하다...^^

또한, 여기서는 기본적인 Web App는 구축되어 있다는 가정과 읽어와야 할 대상이 되는 Log파일이 존재한다는 가정하에 글을 진행하고 있으므로 해당 내용에 대해서 따로 언급하진 않겠다.

그럼 하나씩 알아보자.

먼저 LogTailServlet.java

import java.io.*;

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


public class LogTailServlet extends HttpServlet {
    
    private static final long serialVersionUID = 1L;
 
    private static HashMap mStatusMap = new HashMap();
    private RandomAccessFile raFile = null;
    private long raPointer = 0L;
   
    public void init(ServletConfig config) {
        System.out.println("LogTailServlet: initializing...");
    }

   public  void doGet(HttpServletRequest request, HttpServletResponse  response)
        throws IOException, ServletException {
        doProcess(request, response);
    }

    public  void doPost(HttpServletRequest request, HttpServletResponse  response)
        throws IOException, ServletException {
        doProcess(request, response);        
    }
   
    private void doProcess(HttpServletRequest request, HttpServletResponse response)
                   throws IOException, ServletException {        
        try {
            String fileName = request.getParameter("fileName");
            String action = request.getParameter("action");
            String init = request.getParameter("init");
            if("Y".equals(init)) raPointer = 0L; 
            if ((action != null) && action.equals("checkLog")) {
                readFile(fileName, request, response);
            }
        } catch (Throwable e) {
            System.out.println("LogTailServlet exception :" + e.getMessage());
            e.printStackTrace();
        }
    }
   
    public void destroy(){
    }
   
    public void readFile(String aPath, HttpServletRequest request, HttpServletResponse response){
        try{
            String sLine = null;
            String sRet = "";
            raFile = new RandomAccessFile(aPath, "r");
            if(raPointer <= 0 && raFile.length() > 4096){
                raPointer = raFile.length()-4096;         
            }
            raFile.seek(raPointer);     
            Boolean bTempStatus = null;

            while((sLine = raFile.readLine()) != null) {
                sRet +=  sLine+"<br>"; 
            }
            printOutStatus(sRet,request, response);
            raPointer = raFile.getFilePointer(); 
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private void printOutStatus(String sLine, HttpServletRequest request, HttpServletResponse response)  throws Exception {
        response.setContentType("text/xml");
        response.setHeader("Cache-Control", "no-cache");
        PrintWriter pw = response.getWriter();
        pw.write("<?xml version=\"1.0\" encoding=\"euc-kr\"?>");
        pw.write("<root>");
        pw.write("<message><![CDATA[" + sLine + "]]></message>");
        pw.write("</root>");
        pw.flush();
    } 
}

간단한 Servlet이다.

결국 readFile과 printOutStatus메소드가 주축이 됨을 알 수 있다.

원래 프로젝트 시 구현은 특정 문자열을 찾아서 해당 문자열에 포함된 부분에 대한 HashMap처리를 통해 Server의 Status를 확인하는 절차가 있었으나 여기서는 단순한 Tail부분만 언급하기로 한다.

readFile부분은 위의 소스를 보듯이 RandomAccessFile클래스를 사용하여 FilePointer를 얻어 멤버로 선언된 raPointer에 해당 파일을 읽은 위치까지 기록하고 다음 읽을땐 그 위치부터 읽는 방법을 사용하게 된다.

printOutStatus는 우리가 익히 알고 있는 Ajax Servlet의 기본적인 구현방법을 그대로 따르고 있다.

다만 파일의 로그를 읽는 것이기 때문에 "<" 나 ">"등의 태그가 사용되는 부분이 있을 수 있으므로 CDATA로 처리해 주었을 뿐이다.

물론, 4096으로 선언된 Buffer또한 서버의 성능에 따라 달라져야 함은 물론이다.

by 제이 | 2007/08/20 00:21 | 좋은데..Ajax ㅋㅋ | 트랙백 | 덧글(0)
Ajax로 맹그는 Web Tail(1)

정말 요즈음 들어 Ajax의 매력에 흠뻑 빠진 느낌이다.

물론, 그 극악의 코딩환경은 최악의 평가를 받아 마땅하지만, 적어도 Web Program의 한계를 뛰어넘었다고(사실 뛰어넘은 건 아니다. 다만 뛰어넘은듯 보일뿐...^^) 느끼게 해주는 결과물이야 말로 코딩을 하는 기쁨을 오랜만에 느끼게 해주곤 한다.

요새 들어 시간이 날 때 마다 Ajax로 뭘 할 수 있을까 하는 생각을 많이 하는데 그 생각의 결과와 완벽한 사용자(?)정신으로 무장한 고객님의 요구사항으로 인해 이 강좌의 주제가 탄생할 수 있었다.

먼저, 요구사항의 배경은 이렇다.

1. C/S환경의 Server와 Applet으로 통신을 하는 Web Client가 구축되어 있다.
    - 물론, 왜 그따위 구조냐고 반문하는 분들 많으리라 본다. 그 질문에 대한 답은 4년전에 구축된 프로젝트라는 변명으로 대신하고자 한다. 
    - 그동안 수정안하고 뭐했냐는 질문을 한번 더 하는 독자들이 있다면 그 답은 "니들은 에러없이 잘 구동되고 있는 수준낮은 구조의 은행권 기간계 관련 모듈을 최고의 심플하고 호환성 높은 구조로 바꾸겠다고 할때 니네들 영업이나 고객이 정말 고맙다고 꼭 해달라고 하던?" 이라는 질문으로 대체하고 싶다.

2. 문제는 Applet이 통신한 이후 서버의 로그를 볼 필요가 생기곤 한다.
    - 이 또한, 지금까지 4년동안 한번도 에러가 나거나 문제가 된적은 없었다. 하지만 고객들은 꼭 다시금 확인하고 싶어한다. 뭐 다른 은행권은 모르겠다만 적어도 내 고객은 그랬다.

3. 버뜨, 문제는 지금까지의 고객과 달리 새로이 업무를 맡으신 고객께서 서버로그를 읽으시기 싫고 뭔가 쌈빡한 화면으로 보시고 싶으시다는 데서 출발한다.

여기서 필자는 고민에 빠진다.

1. 로그를 보기 싫다면, 그럼 서버로부터 통신을 받아서 처리결과를 보여줘야 하나?
    - 아냐, 그러려면 서버도 바뀌어야 하고, Applet도 바뀌어야 하는데 넘 공사가 큰데.....ㅜ.ㅜ

2. 아니면, 서버에서 처리된 후 DB로 넣을까? 뭐 화면에서야 DB읽어서 보여주면 되지.
    - 아냐, 이또한 서버가 바뀌어야 하잖아. 게다가 화면을 주기적으로 갱신해줘야 하고.. 깜빡이는거 싫어한단 말야..고객님이..ㅜㅜ

3. 그럼 로그를 화면상에 보이도록 tail프로그램을 하나 사서 깔라고 해?
    - 아냐, 그러면 그 고객이 울 회사에서 날 내쫒을때까지 Complain할꺼야....ㅜ.ㅜ

에잇 그럼 어떻게 하면 되지?
결론적으로 필자가 생각한 방법은 아래와 같다.

1. 사용자의 Action을 받아서 Applet이 통신을 보내기 전, 서버단에서 Tailling하는 서블릿을 구동한다.

2. 해당 서블릿에서는 서버의 로그를 찾고자 하는 문자가 나올때까지 Tailling한다.

3. 이제 Applet에서 Server로 통신을 보낸다.

4. Server에서 처리된 결과를 Log로 쌓는다.

5. Tailling하고 있던 Servlet에서 해당 로그를 Catch한다.

6. Catch된 로그를 사용자 화면에 뿌린다.

좋아!! 바로 이거야 라고 생각하며 좋아했다.
하지만, 잠시 몇가지 문제가 봉착하게 됬다.
먼저, 앞의 시나리오상에서 3, 4번은 이미 구현되어 있는 기능이므로 문제가 되지 않는다.
또한, 1번의 Tailling이야 뭐 짜면된다...ㅋㅋㅋ
문제는 5, 6번이다. 이걸 우찌 해야 하지?

결론은 Ajax였다....^^

화면상에 사용자 모르게 변경사항을 반영할 수 있는 방법이니까 우리 까칠한 고객님은 별말씀 안하고 넘어갈 듯 했다.


---- 1강 End ---

꼬랑지 ~ 여기서 1강을 마친다....절단신공을 발휘함은 블로그의 독자들이 좀 더 쉽게 읽게하고픈 필자의 배려가 담겨있음을 기억해주길 바란다...^__^

by 제이 | 2007/04/17 18:58 | 좋은데..Ajax ㅋㅋ | 트랙백 | 덧글(0)
오랜만이다....슬픈거...
정말 오랜만에 블로그를 다시 시작했다..

아무도 모르는

이 블로그는 내가 누구인지 아무도 모르는 곳이다...

그래서 더 솔직해 질 수 있을지도 모르겠다...

오늘 정말 착한 사람을 다시 만났다

난 그 사람을 슬프게만 했는데....그 사람은 여전히 착했다...

그래서 슬픈걸까?

여전히 같은 모습의 그 사람때문일까?

갑자기 15년전의 내 모습이 떠오는건 왜인지...

마냥 슬퍼진다

그저 아둥바둥대며 살아온 시절은 아니었는지....뒤돌아보게 된다

역시 달라지는 건 없지만....그저 그대로이겠지만...
by 제이 | 2007/04/17 04:21 | Jay's Story | 트랙백 | 덧글(1)
Java Application Windows Service 등록법
일반적으로 Windows 에서의 Java Application은 Command Console을 사용하여 구동된다.
이 방식은 아래와 같은 단점을 가지고 있다.

1. 사용자 실수로 인해 Command Console에 Mouse Click이 발생하면 Application이 Hold됨.
2. 사용자가 Logout하면 해당 Application이 종료됨.
3. 특별히 제약이 없는 경우 터미널 서버나 원격 데스크탑 Application을 사용하여 같은 Application을 각 User별로 계속 구동할 수 있음.

이런 단점을 극복하기 위해 Unix에서는 Background Process로 구동시키면 되지만, Windows는 그 태생이 End User를 위한 DeskTop이기에 그 개념이 없다.

그와 유사한 개념이 Windows Service이다.

그러나 애석하게도 현재 JDK는 Windows Service를 사용하는 API를 제공하고 있지는 않다.

그로 인해 Apache나 InstallAnywhere등의 Application 벤더들은 그들만의 방법으로 Service등록을 가능하게 해서 사용하고 있는 실정이다.

지금 제시할 방법은 이러한 Java Application을 Windows Service로 등록하는 간단한 방법이다.

프로젝트에 적용중인 모듈에 적용하기 위해 여러자료를 찾다 발견한 매우 간단한 방법이다.

지금까지 제가 배포한 모듈의 경우 모두 이 방법을 사용해 서비스에 등록했다.

방법은 다음과 같다.

JavaService.exe 에 구동 Argument로 설정값을 넘겨주면 서비스 등록이 완료되게 된다.

실제 사용은 아래의 방법을 따른다.

1. JavaService.exe를 구한다.
2. 서비스 등록용 batch파일을 만든다. 아래는 필자가 사용하는 파일의 예이다.

C:JavaService -install MyApp %JAVA_HOME%jreinserverjvm.dll -Djava.class.path=.;C:MyAppclasses -start MyApp.StartApp -params INSTALL_DIR=C:MyAppr -out C:MyApplogsstdout.log -err C:MyApplogsstderr.log -auto

3. 서비스 해지용 batch파일을 만든다. 아래는 필자가 사용하는 파일의 예이다.

C:JavaService -uninstall MyApp

더 자새한 내용은 http://javaservice.objectweb.org/docs/description.html을 참고하기 바란다.
by 제이 | 2005/04/25 17:50 | JAVA, JSP 썅!! | 트랙백 | 덧글(4)
MS MOM 리눅스도 관리 - ZDNET 펀글
난 흔히 말하는 SMS 솔루션과 관계된 일을 하고 있다....

솔직히 우리 바닥에서 윈도우는 논외였다.

콘솔만 윈도우면 됬으니까...

크리티컬한 관리대상 장비는 거의 대부분 유닉스나 메인프레임이었기 때문이다

그런데...

아래의 기사는 슬슬 이 시장으로도 M$의 문어발이 접근하고 있다는 생각이 든다....

솔직히 어찌 될지도 궁금하고 겁나기도 하고 어케 대비해야 할지도 고민이다...

어쨋든..그리 멀지 않은 것만은 사실이다.

M$ !! 결국 오는건가?

===================================================================================
윈도우만 편애하던 MS의 CEO 스티브 발머가 마음을 바꾸고, MS가 앞으로 기업들이 보유한 다양한 시스템, 심지어 리눅스가 동작중인 시스템이라도 관리가 용이하도록 지원할 예정이라고 밝혔다.

발머는 지난 20일 그간 MS의 핵심 관리 제품인 오퍼레이션 매니저 소프트웨어에서 비(非) 윈도우 머신에 대한 지원을 강화해달라는 고객들의 요청에 귀를 기울여왔다고 밝혔다. 또한 MS는 올해 말 내놓을 버추얼 서버 제품의 서비스 팩 업데이트에서 리눅스 기반의 버추얼 머신 구동에 대한 지원을 강화할 계획이라고 말했다.

발머가 기조 연설을 하는 동안 레드햇 엔터프라이즈 리눅스가 동작중인 버추얼 서버가 시연됐는데, 연설 도중 발머는 "인정하고 싶지는 않지만 그것이 버추얼 서버 고객들에게 중요한 기능이라는 걸 안다"라고 말했다.

오직 고객을 위해?
현재 버전에서도 리눅스나 다른 운영체제를 구동할 수는 있지만, MS는 고객이 윈도우가 아닌 운영체제를 구동해 곤란을 겪을 때도 지원하겠다는 계획을 세운 것이다.

MS는 MS 오퍼레이션 매니저(MOM)로 솔라리스 서버를 제어하는 것을 시연하면서, MOM 콘솔 내에 팝업 형태로 알림 메시지가 나타나는 것을 보여주기 위해 솔라리스가 동작중인 시스템에서 냉각팬을 뽑아버리기도 했다.

발머는 "우리는 썬과 긴밀히 작업해왔다. 알고 있겠지만 전에는 썬과의 긴밀한 협력은 생각할 수도 없었다"라고 말하며, 1년 전 양사가 상호 긴장 완화를 한 이래 두 회사가 함께 작업한 그간의 성과를 기반으로 업데이트를 곧 내놓을 것이라고 덧붙였다.

WS-매니지먼트 웹 서비스 표준을 통해 솔라리스 서버 장비를 모니터링하는 데 MOM을 사용한다는 것은, MOM 관리 프로그램이 관리 대상이 윈도우 머신인지 알 필요가 없다는 의미다.

MS의 인프라스트럭처 서버 마케팅 부문을 총괄하고 있는 밥 켈리는 반대 상황도 함께 시연하면서 "보는 것처럼 솔라리스가 동일한 프로토콜을 이용해 윈도우 머신을 이렇게 쉽게 관리할 수 있다"라고 말했다.

MS의 상호운용성 부족하지 않다
켈리는 “MS의 목표는 윈도우가 수많은 윈도우와 비 윈도우 시스템들을 관리하는 데 가장 비용이 적게 드는 방법을 제공한다는 것을 확실히 보여주는 것”이라고 말했다. 그는 이 관리 소프트웨어가 새로 나온 것이지만 상호 운용성을 이미 제공하고 있다며, 이런 점은 MS가 그 동안 충분히 신뢰를 얻지 못했던 부분이라고 말했다.

켈리는 "우리 제품이 다른 회사 제품과 상호 운용이 되지 않는다는 것을 알고 있다는 것이 정말로 '부끄러운 일'"이라고 말했다.

발머는 20일 연설에서 차세대 데스크톱용 윈도우 제품인 '롱혼'에 대해서도 언급했다. 그는 롱혼이 내년 하반기에 나오면 관리 편의성이 나아지고 운영 비용이 좀더 저렴해질 것이라며, 윈도우 운영체제 최고 책임자인 짐 알친이 지난 주에 언급한 내용을 여러 번 되풀이하며 강조했다.

또한 발머는 롱혼 데스크톱 버전이 출시된 후 약 6개월 뒤에 롱혼 서버가 나올 예정이라며 롱혼 서버 출시 시기에 대해 좀더 자세히 언급했다. 역사적으로 볼 때 서버를 출시하기 전 테스트 기간이 비교적 오래 걸리기 때문에 정확한 스케줄을 맞추는 것을 기대하기는 어려웠다.

켈리는 롱혼의 클라이언트 버전과 서버 버전의 개발 주기를 맞춰가고 있다고 말했다. 이는 서버 출시에 필요한 추가 테스트에 6개월이라는 시간이 필요하다는 것을 의미한다. 그는 "이를 지키기 위해 최선을 다하고 있다"라고 덧붙였다.

발머의 이야기는 MS가 단일화된 시스템 센터 제품을 내놓기로 한 계획을 파기하고, 대신 기존 MOM과 시스템 매니지먼트 서버(SMS) 제품에 대한 업데이트 계획을 세우며 향후 관리 소프트웨어를 어떻게 내놓을지 밝힌 지 하루 만에 나온 것이다.
by 제이 | 2005/04/25 13:54 | 이건 또 먼데? | 트랙백 | 덧글(0)
질렀다 알육이

이동건이 넘 부러웠었나 보다...ㅜ.ㅜ

질렀다...PDA폰

전에 쓰던 전화를 3년 6개월을 썼다....
바꿀때가 되었다고 생각하던 차에 지름신이 강림하셔서 그만.....ㅜ.ㅜ

잘써야지...한 3년 쓰면 되겠지..머..쩝
by 제이 | 2005/04/21 19:14 | 끄적끄적 | 트랙백 | 덧글(0)
SJSXP를 이용한 XML Programming(Java 4월 19일 Tech Tip 펀글)
SUN JAVA STREAMING XML PARSER 소개

현재 dom4j를 사용해서 프로젝트를 수행하고 있다.
성능도 괜찮고 API도 잘 되어 있어서 괜찮다고 생각하고 있었는데 메일로 날아온 SJSXP TechTip을 봤는데 JDBC API를 사용하는 듯한 쉬운 사용법이 일단 맘에 든다.
새로운 프로젝트에 적용해보아야 할듯.....

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

XML을 사용하는 대부분의 Java 개발자는 SAX (Simple API for XML) 라이브러리와 DOM(Document Object Model) 라이브러리에 익숙할 것이다. SAX는 이벤트 기반의 API이며, 이는 일반적으로 프로그래머가 파서와 몇 개의 리스너를 등록하고, 특정 XML 문법 생성자(예를 들어 요소나 속성)가 도착되면 리스너 메소드가 호출된다는 의미이다. 그 반대로 DOM은 트리 기반의 아케텍처를 가지며, 전체 문서를 스캔하여 마주치는 각각의 문법 생성자의 오브젝트 트리를 구축한다. 프로그래머는 스캔이 완료된 후 오브젝트 트리에 접근하여 수정할 수 있다

이 두 방법 모두 각각의 결점을 가지고 있다: 리스너를 이용하는 이벤트 기반의 API는 일반적으로 다루기 힘들다. 리스너들이 파서에 의해 조종되기 때문이다. 트리 기반의 API는 스캔되는 문서의 양에 비해 과도한 양의 메모리를 소모할 수 있다. 이제 Java 개발자들이 XML 을 스캔하는데 이용할 수 있는 세 번째 API가 등장하였다. StAX (Streaming API for XML parse)가 그것이다.

SJSXP란?

SJSXP(Sun Java Streaming XML Parser)는 StAX를 빠른 속도로 구현한다. 썬마이크로시스템즈와 협력하고 있는 BEA Systems, XML-guru James Clark, Stefan Haustein, Aleksandr Slominski (XmlPull 개발자들), 그리고 JCP의 다른 멤버들은 JSR 173를 구현하는 것으로써 StAX를 개발하였다. StAX은 공유 인터페이스들에 기반하는, 파서(parser) 독립적인 Java API이다.

SJSXP는 Java Web Services Developer Pack v1.5에 포함되어있다. SJSXP에 대해 처음으로 알아차릴만한 것은 이것이 스트림 API에 기반한다는 것이다. 즉, 개발자가 어느 한 노드에 접근하기 위해 전체 문서를 읽을 필요가 없다. 또한, 파서를 시작하여 파서가 데이터를 이벤트 리스너 메소드에 "push"하도록 허용하는 법칙을 따르지 않는다. 대신에 SJSXP는 "pull" 메소드를 구현하며, 이 메소드는 정렬 포인터를 문서에서 현재 스캔되고 있는 지점에 유지한다. 이는 종종 커서(cursor)라고 불린다. 사용자는 단순히 커서가 현재 가리키고 있는 노드에 대한 파서를 요청하면 된다.

XML 문서 파싱에 SJSXP 이용하기

SJSXP를 이용하여 XML 문서를 읽는 것은 아주 쉽다. 대부분의 작업은 javax.xml.stream.XMLStreamReader 인터페이스를 구현하는 오브젝트를 통해 이뤄진다. 이 인터페이스는 XML 문서의 첫 부분에서 마지막까지 이동되는 커서를 나타낸다. 몇 가지 명심해야할 것이 있다. 커서는 항상 하나의 아이템(시작 태그 요소, 진행 명령, DTD 선언 등)만을 가리켜야 한다. 또한 커서는 항상 앞으로 움직여야 하며 (뒤로 움직일 수 없다), 다음에 무엇이 나타나는지 미리 보기를 실행할 수 없다. 다음의 코드 발췌에서 파일로부터 XML을 읽는 XMLStreamReader를 얻을 수 있다.

URL url = Class.forName("MyClassName").getResource(
"sample.xml");
InputStream in = url.openStream();
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader parser = factory.createXMLStreamReader(in);


그 후 다음 코드를 이용하여 XML 파일을 반복할 수 있다.

while(parser.hasNext()) {

eventType = parser.next();
switch (eventType) {

case START_ELEMENT:
// Do something
break;
case END_ELEMENT:
// Do something
break;
// And so on ...
}
}


XMLStreamReader의 hasNext() 메소드는 XML 파일에서 또다른 유효한 아이템이 있는지 확인한다. 만약 있다면, 커서가 다음 아이템으로 넘어가게하기 위해 next() 메소드를 사용할 수 있다. next() 메소드는 만나게되는 문법상 생성자(아이템)의 타입을 가리키는 인티거 코드를 리턴한다.

XMLInputStreamReader에는 몇 개의 get 메소드가 있어서 커서가 가리키는 XML 아이템의 내용을 얻는 데 사용할 수 있다. 첫번째 메소드는 getEventType()이다:

public int getEventType()

메소드는 커서가 있는 곳에서 파서가 찾은 아이템의 타입을 식별하는 integer 코드를 리턴한다. next() 메소드에 의해 리턴되는 것과 같은 코드이다. 아이템은 XMLInputStream 상수 중 하나에 의해 식별된다.

XMLStreamConstants.START_DOCUMENT
XMLStreamConstants.END_DOCUMENT
XMLStreamConstants.START_ELEMENT
XMLStreamConstants.END_ELEMENT
XMLStreamConstants.ATTRIBUTE
XMLStreamConstants.CHARACTERS
XMLStreamConstants.CDATA
XMLStreamConstants.SPACE
XMLStreamConstants.COMMENT
XMLStreamConstants.DTD
XMLStreamConstants.START_ENTITY
XMLStreamConstants.END_ENTITY
XMLStreamConstants.ENTITY_DECLARATION
XMLStreamConstants.ENTITY_REFERENCE
XMLStreamConstants.NAMESPACE
XMLStreamConstants.NOTATION_DECLARATION
XMLStreamConstants.PROCESSING_INSTRUCTION


만약 아이템에 이름이 있는 경우, getName()과 getLocalName() 메소드를 사용하여 이름을 얻을 수 있다. 후자는 어떤 다른 정보(예; 확인된 네임스페이스가 없는 요소의 이름) 없이 이름 자체를 산출한다.

public Qname getName()
public String getLocalName()


만약 당신이 현재 아이템의 네임스페이스를 식별하고 싶다면 getNamespaceURI() 메소드를 사용 할 수 있다.

public String getNamespaceURI()

만약 DTD 선언 내의 텍스트나 요소 안의 텍스트 등과 같은 동반되는 텍스트가 있을 때에는 다음 메소드들을 사용하여 얻을 수 있다.(후자는 요소를 위해 단독으로 사용 될 수 있다)

public String getText()
public String getElementText()


만약 요소가 그것과 관계 되는 속성을 가진다면 getAttributeCount() 메소드를 사용하여 현재 요소가 가진 속성들의 갯수를 얻을 수 있다. 그 후 getAttributeName() 와 getAttributeValue() 메소드를 사용하여 각각의 정보를 검색 할 수 있다.

public int getAttributeCount()
public Qname getAttributeName(int index)
public String getAttributeValue(int index)


만약 속성의 로컬 이름과 요소의 네임스페이스 URI를 안다면, 또한 다음의 메소드를 이용하여 속성값을 얻을 수 있다.

public String getAttributeValue(
String elementNamespaceURI, String localAttributeName)


추측했겠지만, 모든 접근자 메소드가 특정 상태에 적용가능한 것이 아니다. 예를 들어, 현재 DTD를 프로세싱 중이라면 getElementText()을 호출할 수 없다. 만약 이를 호출한다면, 파서가 충돌하는 이벤트 타입을 식별했다는 XMLStreamException을 얻게 되거나 메소드가 스스로 널(null) 값을 리턴하게 될 것이다.

XMLInputFactory 클래스의 setProperty() 메소드를 사용하여 몇 가지 파서 속성을 시작할 수 있다. 예를 들어, 다음은 파서와 마주치는 엔티티 레퍼런스는 교체될 것이라고 지정한다.

factory.setProperty(
XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
Boolean.TRUE);


파서가 외부 엔티티를 지원하는 것을 막기 위해 다음과 같이 설정한다.

factory.setProperty(
XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
Boolean.FALSE);


파서가 네임스페이스를 알아차리게하기 위해 다음과 같이 설정한다.

factory.setProperty(
XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE);


SJSXP의 현재 버전에서는 다음의 명령은 허용되지만 파서는 확인되지 않는다는 것을 유의하기 바란다.

factory.setProperty(XMLInputFactory.IS_VALIDATING,
Boolean.TRUE);


만약 이 XMLInputFactory 특성들 중 어느 하나라도 가능하게 된다면 setXMLReporter() 메소드를 사용하여 파서가 만나게되는 오류를 제어할 수 있다. 파서가 만나는 오류의 타입을 가장 빨리 정확하게 결정할 수 있는 방법은 setXMLReporter() 메소드와 상호 작용하는 익명의 이너 클래스를 사용하는 것이다. 이는 다음과 같다.

factory.setXMLReporter(new XMLReporter() {
public void report(String message, String errorType,
Object relatedInformation, Location location) {
System.err.println("Error in "
+ location.getLocationURI());
System.err.println("at line "
+ location.getLineNumber()
+ ", column " + location.getColumnNumber());
System.err.println(message);
}
});


XML 문서 작성에 SJSXP 사용하기

XML 결과를 작성하는 것은 SJSXP를 이용하면 쉽다. 이 경우, XMLStreamReader 인터페이스 대신에 XMLStreamWriter 인터페이스를 사용할 수 있다. XMLStreamWriter 인터페이스는 작성하는 요소, 속성, 코멘트, 텍스트를 비롯해 XML 문서의 모든 부분을 작성하기 위한 직접적인 메소드를 제공한다. 다음의 예제에서는 어떻게 이 인터페이스를 얻어서 XML 문서를 작성하는데 사용하는지 보여준다.

XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter xtw =
xof.createXMLStreamWriter(new FileWriter("myFile"));

xtw.writeComment(
"all elements here are in the HTML namespace");
xtw.writeStartDocument("utf-8","1.0");
xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement(
"http://www.w3.org/TR/REC-html40","html");
xtw.writeNamespace(
"html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement(
"http://www.w3.org/TR/REC-html40","head");
xtw.writeStartElement(
"http://www.w3.org/TR/REC-html40","title");
xtw.writeCharacters("Java Information");
xtw.writeEndElement();
xtw.writeEndElement();

xtw.writeStartElement(
"http://www.w3.org/TR/REC-html40","body");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40","p");
xtw.writeCharacters("Java homepage is ");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40","a");
xtw.writeAttribute("href","http://java.sun.com");
xtw.writeCharacters("here");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndDocument();

xtw.flush();
xtw.close();


각 요소를 작성하는 것이 끝나면 사용자는 라이터(writer)를 날려보내고 닫아야한다.

이전의 코드는 다음의 XML로 결과가 나타난다.(여기서는 쉽게 읽을 수 있게 라인 별로 나타내었다.)

<!--all elements here are explicitly in the HTML namespace-->
<?xml version="1.0" encoding="utf-8"?>
<html:html xmlns:html="http://www.w3.org/TR/REC-html40">
<html:head>
<html:title>Java Information</html:title>
</html:head>
<html:body>
<html:p>
Java information is
<html:a href="http://frob.com">here</html:a>
</html:p>
</html:body>
</html:html>


XML 문서 필터링

만약 각각의 아이템 타입을 스캔하고 싶지 않다면 들어오는 XML 문서에 대한 필터를 생성할 수 있다. 이를 위해서는 javax.xml.stream.StreamFilter 인터페이스를 구현하는 클래스를 생성한다. 이 인터페이스는 단지 accept() 메소드만으로 구성된다. 이 메소드는 XMLStreamReader 오브젝트를 허용하고 원시 Boolean을 리턴한다. StreamFilter의 일반적인 구현은 다음과 같다.

public class MyStreamFilter implements StreamFilter {

public boolean accept(XMLStreamReader reader) {
if(!reader.isStartElement() && !reader.isEndElement())
return false;
else
return true;
}
}


그 다음으로 XMLInputFactory의 createFilteredReader() 메소드를 호출하여 필터링된 리더(reader)를 생성하고 이를 원래 XML 스트림 리더와 StreamFilter 구현에 모두 전달한다. 다음과 같다.

factory.createFilteredReader(
factory.createXMLStreamReader(in), new MyStreamFilter());


SJSXP의 더 자세한 정보는 Sun Java Streaming XML Parser release notes를 참고하기 바란다.

Sun Java Streaming XML Parser 예제 코드 구동하기

이번 테크팁에 대한 샘플 아카이브(ttfeb2005sjsxp.jar)를 다운로드한다.


Java Web Services Developer Pack Downloads page에서 Java WSDP 1.5를 다운로드, 설치한다.


예제 압축파일을 다운로드 받은 디렉토리를 변경하고, 다음과 같이 샘플 아카이브에 대한 JAR 파일의 압축을 푼다.

jar xvf ttfeb2005sjsxp.jar

사용자의 classpath가 ttfeb2005sjsxp.jar와 jsr173_api.jar를 포함하도록 설정한다. 이들은 Java WSDP 1.5 installation의 sjsxp/lib 디렉토리에 위치하고 있다.


SJSXPInput를 컴파일하고 구동한다.

다음의 각각의 XML 아이템과 유사한 엔트리가 나타날 것이다.
Event Type (Code=11): DTD
Without a Name
With Text: Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-
transitional.dtd">
-----------------------------


SJSXPOutput를 컴파일하고 구동한다. 결과는 XMLOutputFile라는 이름의 파일에 보내지며, 위의 결과 예제에 보여지는 요소들을 포함할 것이다.
by 제이 | 2005/04/19 13:41 | JAVA, JSP 썅!! | 트랙백 | 덧글(0)
resin install on UNIX

1.Resin Install Command Line Argument

2.resin.conf(1)

3.resin.conf(2)

4.Resin Install on UNIX

5.How to Start with Appache

6.How to Start with IIS

7.Resin3.0 변경사항

Resin 역시 Java Application 이기 때문에 “Write Once, Run Everywhere” 를 추구한다.

그러므로 Unix에서 구동하기 위해 특별히 make해야 한다거나 소스를 수정해야 하는 것은 필요치 않다. 즉, JDK만 설치되어 있다면 동일하다는 뜻이다.

아래 문서의 내용은 Linux와 Mac OS-X도 함께 포함된다.

다만, Daemon이기 때문에 기본적으로 Background로 구동되어야 하고 그 구동방법이 Windows의 그것과는 다르기에 필요한 몇 가지 설정이 있을 뿐이다.

Unix에 Install하기 위해 고려할 사항은 아래와 같다.

1. Resin 사용자 계정 생성

2. JDK1.4이상 설치여부

3. Perl 5.6 이상 설치 여부(for $RESIN_HOME/bin/httpd.sh)

4. rc.d 등록여부

이 이외에 resin.conf설정이나 web-app 의 deploy방법, command-line argument사용법 등은 windows의 그것과 동일하며 이미 앞선 글에서 설명되었으므로 생략하도록 하겠다.

1. Resin 사용자 계정 생성

A. Install Guide에 의하면 Resin을 구동하기 위한 특별한 계정은 따로 존재하지 않는다.(꼭, Root 이어야 할 필요는 없다. 오히려 보안정책상 일반 User를 추천한다.)

B. 필요하다면 JAVA_HOME 과 RESIN_HOME을 설정한다.(구성되어 있지 않다면 설정하는 것이 좋다.)

2. JDK1.4이상 설치여부

A. Caucho에서는 JDK1.4이상을 권고하고 있으며 version확인법은 아래와 같다.

# java –version

java version "1.4.2_06"

Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_06-b03)

Java HotSpot(TM) Server VM (build 1.4.2_06-b03, mixed mode)

B. 주의할 것은 JRE만 설치되어서는 안되고 JDK가 설치되어야 한다는 것이다. 위의 내용은 JRE만 Check 할 수 있을 뿐이다.

# javac

Usage: javac

where possible options include:

-g Generate all debugging info

-g:none Generate no debugging info

-g:{lines,vars,source} Generate only some debugging info

-nowarn Generate no warnings

-verbose Output messages about what the compiler is doing

-deprecation Output source locations where deprecated APIs are used

-classpath Specify where to find user class files

-sourcepath Specify where to find input source files

-bootclasspath Override location of bootstrap class files

-extdirs Override location of installed extensions

-d Specify where to place generated class files

-encoding Specify character encoding used by source files

-source Provide source compatibility with specified release

-target Generate class files for specific VM version

-help Print a synopsis of standard options

C. 위와 같이 JDK 설치도 확인되어야 한다.

D. 각 OS별로 JDK설치 방법은 다르므로 관련 OS의 Tech note를 참조하기 바라며 이 글에서는 언급하지 않겟다.

3. Perl 5.6이상 설치여부

A. Unix에서 resin을 구동하는 방법은 $RESIN_HOME/bin/httpd.sh를 사용하는 것이다.

B. Httpd.sh의 내용은 주석부분을 제거하면 아래와 같다.

#! /bin/sh

#

# Extra arguments to Java. If you're passing arguments to the JVM, you'll

# need to use -Jxxx. For example, args="-J-ms48m". You can modify

# the pid file with args="-pid server-a.pid"

#

args=

# class to start

class=com.caucho.server.http.HttpServer

# name of the server

name=httpd

# location of perl executable

perl=perl

# trace script and simlinks to find thw wrapper

script=`/bin/ls -l $0 | awk '{ print $NF; }'`

while test -h "$script"

do

script=`/bin/ls -l $script | awk '{ print $NF; }'`

done

bin=`dirname $script`

exec $perl $bin/wrapper.pl -chdir -name "$name" -class "$class" $args $*

결과적으로 보면 httpd.sh가 있는 디렉토리를 구해서 perl을 사용해서 wrapper.pl이라는 파일을 구동시키고 있음을 알 수 있다.

위의 파일에 포함된 실행옵션들은 글 말미에 별도 설명하고 여기선 생략한다.

자세히 해석하길 원한다면 Unix shell script에 관한 자세한 자료를 참조하길 바란다.

C. 위에서 언급한 wrapper.pl의 내용은 아래와 같다.

i. Wrapper.pl은 약 800여 line의 perl파일이다. 해당 파일의 내용을 하나씩 해석하는 것은 Perl script 사용법을 언급하며 설명하는 것이고 이 글의 범위를 벗어나게 되므로 지양한다.

ii. 다만, 관련 파일의 내용에 대해 나열하고 주의사항을 언급한다.

iii. Wrapper.pl의 내용은 아래와 같다.

1. Resin의 start, stop, restart

2. JAVA_HOME설정

3. RESIN_HOME설정

4. configuration file의 변경

5. CLASSPATH설정

6. native thread로 강제 구동

7. green thread로 강제 구동

8. jit 컴파일러 사용여부

9. stdout log file의 변경

10. stderr log file의 변경

11. pid file 설정

12. 자동 restart 여부

iv. Wrapper.pl관련 오류 및 조치사항

1. 먼저 wrapper.pl구동을 위한 Perl은 Version 5.6이상을 권고하고 있으며 확인방법은 아래와 같다.

# perl -v

This is perl, v5.6.1 built for sun4-solaris-64int

(with 48 registered patches, see perl -V for more detail)

Copyright 1987-2001, Larry Wall

2. 아래의 내용은 perl이 설치되어 있지 않거나 perl 버전이 낮은 경우 발생한다.

# ./httpd.sh start

usage: dirname [ path ]

Can't open perl script "/wrapper.pl": No such file or directory

3. 아래의 내용의 원인도 역시 perl이 설치되지 않았거나 version이 맞지 않아서 발생하며 참고로 위의 내용은 HP-UX11에서 발생한 내용이다.

# ./httpd.sh

syntax error in file ./wrapper.pl at line 9, next 2 tokens "use File"

syntax error in file ./wrapper.pl at line 72, next 2 tokens "my("

syntax error in file ./wrapper.pl at line 73, next 2 tokens "fileparse("

syntax error in file ./wrapper.pl at line 517, next 2 tokens "0 and"

syntax error in file ./wrapper.pl at line 527, next token "}"

syntax error in file ./wrapper.pl at line 634, next 2 tokens "setpgrp;"

syntax error in file ./wrapper.pl at line 706, next 2 tokens "my $file "

syntax error in file ./wrapper.pl at line 715, next 2 tokens "$dir or"

syntax error in file ./wrapper.pl at line 717, next 2 tokens "$pwd or"

syntax error in file ./wrapper.pl at line 724, next 2 tokens "$dir or"

./wrapper.pl has too many errors.

4. 아래의 내용은 resin이 사용하는 pid파일을 write하지 못할 때 발생하는 내용이다. 해결방법은 해당 디렉토리와 파일의 권한을 확인하고 재설정하도록 한다.

# ./httpd.sh start

Can't locate Socket.pm in @INC at /resin-2.1.4/bin/wrapper.pl line 10.

BEGIN failed--compilation aborted at /resin-2.1.4/bin/wrapper.pl line 10.

4. rc.d등록여부

A. rc.d관련 설정은 해당 시스템 관리자의 role이므로 기본적으로는 권한이 있는 관리자에게 설정해달라고 하는 것이 옳다.

B. Caucho의 권고사항은 아래와 같다.

httpd.sh를 resin/bin에 resin-a.sh로 이름을 바꾸고 권한을 할당하여 복사한다.

resin-a.sh에 JAVA_HOME, RESIN_HOME, PATH, "-pid" 를 설정한다.

"resin-a.sh start" 와 "resin-a.sh stop”을 root 계정으로 test한다.

아래와 같이 수행한다.

"ln -s /usr/local/resin/bin/resin-a.sh /etc/rc.d/rc3.d/S86resin-a"

"ln -s /usr/local/resin/bin/resin-a.sh /etc/rc.d/rc5.d/S86resin-a"

"ln -s /usr/local/resin/bin/resin-a.sh /etc/rc.d/rc2.d/K14resin-a"sr/local/resin/bin/resin-a.sh /etc/rc.d/rc3.d/S86resin-a"

"ln -s /usr/local/resin/bin/resin-a.sh /etc/rc.d/rc5.d/S86resin-a" "ln -s /usr/local/resin/bin/resin-a.sh /etc/rc.d/rc2.d/K14resin-a"

by 제이 | 2005/04/18 19:05 | JAVA, JSP 썅!! | 트랙백 | 덧글(1)
resin command line argument
이글루는 표가 지원안된다...
간신히 미리보기에서 제대로 보이게 만들었더만 정작 올리니까 깨진다.
결국 사내 intranet에 올린 걸 그림으로 떠서 붙이기로 했다...쩝
by 제이 | 2005/04/18 17:12 | JAVA, JSP 썅!! | 트랙백 | 덧글(0)
ITIL Foundation
ITSMF korea에서 하는 첫 포럼 기념의 행사였던 ITIL Foundation교육을 받았다.

2일간 교육과 마지막날 인증시험이 있었고 꽤나 힘들게 공부했다.

내용은 쉬웠지만 너무 짧은 기간이 문제였다.

떨어질까 겁났는데 합겼했다....^^

4일 과정이라면 무난할 듯 하다.

올해 목표가 PMP, CISSP, ITIL Foundation이었는데 일단 하나는 이룬셈이다.

이제 PMP를 향해 달려가야겠다....이건 좀 더 공부해야겠지?...^^
by 제이 | 2005/04/15 13:35 | 끄적끄적 | 트랙백 | 덧글(2)
resin.conf(2)
이번 내용은 지난주에 언급한 바와 같이 다중 서비스 구축 방법에 대해서 알아보겠다.

1. 다중서비스 구축방법

이 문제는 resin이 standalone으로 구동된다는 전제하에 해당되는 것이다. 만약 Apache나 IIS에 plugin으로 구동시키고 있다면 두 개의Instance를 띄운다는 것은 Apache와 IIS의 설정과 관련된 일이므로 이 문서의 범위 밖의 일이므로 언급하지 않겠다.

두 개 이상의 instance를 사용하는 방법
현재 resin은 기본적으로 단일 Java 프로세스로 구동되며 내부적으로 Client의 요청마다 Thread를 생성하여 구동하는 기본적인 WebServer구조를 유지하고 있다.

즉, 이 말은 두 개의 instance를 띄운다는 것은 (Oracle에서의 Instance개념과 달리) 두 개의 Java Process를 각각 구동시킨다는 의미가 된다.

그러므로 두 개의 프로세스를 구동시키기 위해서는 Unix나 Windows 모두 권한이 있는 계정으로 접속한 후 command를 통해 여러개의process를 띄우면 되겠으나 두가지 고려할 사항이 있다.

1. port 설정

만약, 같은 port로 두 개를 띄우고 각각 자동적으로 서비스 되길 바란다면 그것은 두 개의 서비스가 아니라 하나의 서비스에 load balancing을 위한 설정을 하겠다는 뜻이다.

그러므로 두 개의 instance를 띄운다는 것은 각각 port가 다르게 설정되어야 함을 의미한다. port가 다르다는 것은 두 개의 resin.conf 파일이 각각 다르다는 것을 의미한다.

즉, $RESIN_HOME/bin/httpd -install -conf $파일명을 사용하여 각각 다른 conf파일을 지정해야 할 것이다.

2. Windows Service명 중복

Unix의 경우 어차피 Daemon Process로 구동되도록 명령을 수행할때, 같은 process명이라도 pid가 다르므로 상관없이 둘 다 daemon 으로 구동되겠지만 Windows의 경우 Background process로 구동하기 위해 service로 구동할 때는 service명이 같으면 안된다는 문제가 있다.

이 문제는 서비스 명을 다르게 주어서 등록하는 방법을 사용해 해결하면 된다.

즉, $RESIN_HOME/bin/httpd -install-as $서비스명을 사용하여 구동하여 중복된 이름을 방지할 수 있다.

이상의 두가지 문제는 이미 올렸던 글 1.Resin Install Command Line Argument을 참조하면 알 수 있다.

다중 web-app 와 가상호스트
지난 글에도 언급했지만 resin.conf의 구조는 아래와 같다.

<caucho.com>
<http-server>
<host id='gryffindor.caucho.com' app-dir='gryffindor'>
<web-app id='/'>
<servlet-mapping .../>
...
</web-app>
<web-app id='/test' app-dir='/usr/local/test'>
...
</web-app>
</host>
<host id='slytherin.caucho.com' app-dir='slytherin'>
...
</host>
...
</http-server>
</caucho.com>


이 구조를 잘 살펴보면 하나의 http-server에 두개 이상의 host가 존재하며 하나의 host에 두개 이상의 web-app가 존재함을 알 수 있다.

즉, 아래의 그림으로 설명할 수 있다.



위의 그림을 보면 의문점이 생길 것이다.

맨 아래쪽의 접근 방식을 보면 하나의 web-app를 사용할 때도 위와 같이 디렉토리명을 사용해서 접근이 가능한데 무엇이 다른가 하는 것이다.

다른점은 다른 app로 작동하느냐 이다.

즉, 하나의 web-app를 사용할 때는 아무리 디렉토리별로 다르게 접근한다고 해도 모두 같은 session을 공유하고 같은 WEB-INF의 class를 사용하도록 되어 있다.

하지만, 위와 같은 경우는 각기 다른 session을 가지며, 또한 WEB-INF 역시 각기 달리 갖는다. 완전히 독립적인 app로 인식한다는 것이다.

게다가 web.xml을 사용할 경우도 역시 각기 다른 web.xml을 사용해야 함은 물론이다.

그렇다면 가상호스트는 무엇인가?

위의 그림에서 하나의 서버에 두 개의 host가 존재한다

각각은 고유의 도메인을 가지고 있지만 같은 host의 같은 ip를 사용한다.

즉, 글자 그대로 가상의 호스트처럼 동작한다는 의미이다.

일단 사용자의 hosts파일은 아래와 같다고 가정한다.

/etc/hosts

127.0.0.1 localhost

127.0.0.1 www.sms.co.kr

127.0.0.1 www.maxigent.co.kr


사용자는 www.maxigent.co.kr로 접속을 시도한다. 그러면 먼저 사용자의 hosts파일을 검색해서 해당 도메인에 대한 정보에 해당하는 ip로 접속을 시도한다.

만약 해당정보가 없다면 해당 user의 시스템에 설정된 DNS서버에 quering을 하게 될 것이다.

이 부분에 대한 자세한 정보를 원한다면 internetworking의 구조에 대해 참조하기 바란다.

resin은 해당 request가 들어오면 자신의 conf파일을 검색해서 host id와 mapping을 시도한다.

그 결과 해당하는 host id가 존재하면 그부분의 web-app를 반환하게 된다.

결론적으로 기본적인 virtual hosting을 위한 resin.conf는 아래와 같다

<http-server>
...
<host id='sms.sds.co.kr'>
<app-dir>/home/www/sms/docs</app-dir>
...
</host>
<host id='maxigent.sds.co.kr'>
<app-dir>/home/www/maxigent/docs</app-dir>
...
</host>


이 이외에 Virtual Host with Apache or IIS, JVM per Virtual Host, IP-Based Virtual Hosting등의 좀더 깊이있는 주제는 http://www.caucho.com/resin-2.1/ref/virtual-host.xtp 을 살펴보기 바란다.

각 방법별 적용
위에서 두 개 이상의 web-app를 구동하는 방법을 알아봤다.

대부분의 site에서는 위와 같은 방법을 사용하여 운영할 이유는 특별히 없을 것이다.

그러나, 개발중인 경우나 test나 BMT등의 daemo용으로 구축을 요할 때 위의 방법중 하나를 사용하는 것은 필요하다.

위의 방법중 첫 번째 resin을 두 개 이상 구동하는 것을 추천한다.

물리적인 장비의 성능 및 용량이 충분하다면 이 방법을 사용하여 운영중이거나 혹은 다른 개발중인 app에 영향없이 다른 app를 구동할 수 있기때문이다.

물론, virtual host나 web-app를 두 개 이상을 두는 것도 생각해 볼 수 있는 방법이긴 하나 일단 web-app를 두 방법 모두 해당 resin instance의 성능을 나눠 사용하는 것이므로 먼저 운영중인 app에 영향이 생기는 것은 부정할 수 없다.

또한, web-app를 나눠 사용하는 경우 doc-root가 같기 때문에 개발자의 실수로 인한 다른 app의 문제 발생 소지를 가지고 있다.

virtual host의 경우 일단 DNS를 설정해야 한다는 문제가 있고, 그렇지 않을 경우 ip-based로 설정해야 하는데 이것 또한 장비의 NIC가 두 개 이상이어야 한다는 제한이 생기게 된다.

즉, 두 개 이상의 서비스가 필요하다면 장비의 성능이 제공하는 범위에서 각기 다른 process로 구동하라는 것이 결론이다.

장비의 성능이 문제가 된다면 -Xms, -Xmx -Xss등의 메모리 관련 command line argument와 <thread-max>, <thread-keepalive>, <request-timeout>, <accept-buffer-size>, <thread-min> 등의 값을 조절하면 될 것이다.
by 제이 | 2005/04/15 13:31 | JAVA, JSP 썅!! | 트랙백(2) | 덧글(2)
resin.conf(1)
Resin.conf는 크게 아래와 같은 구조를 갖는다.
일반적으로 resin.conf의 Depth는 아래의 예를 따른다.

<caucho.com>
<http-server>
<host id=''>
<web-app id='/'>
<servlet-mapping url-pattern='/test' .../>
</web-app>
</host>
</http-server>
</caucho.com>


대부분의 중요한 설정은 web-app에 속해 있다.
Server는 다수의 host element를 포함하며 각 host 는 다수의 web-app 들을 포함할 수 있다.
각 host 는 가상host를 설정하고 각 web-app 는 application을 설정한다.

<caucho.com>
<http-server>
<host id='gryffindor.caucho.com' app-dir='gryffindor'>
<web-app id='/'>
<servlet-mapping .../>
...
</web-app>

<web-app id='/test' app-dir='/usr/local/test'>
...
</web-app>
</host>

<host id='slytherin.caucho.com' app-dir='slytherin'>
...
</host>

...
</http-server>
</caucho.com>


Simple configurations host 와web-app tag의 생략을 허용한다. 만약 host 와 web-app를
생략한다면 default host 와 default application를 사용하게 되며 http-server element 의 모든 web-app에 동일한 설정을 적용하게 된다.

<caucho.com>
<http-server app-dir='/usr/local/web'>
<servlet-mapping .../>
...
</http-server>
</caucho.com>


1. <caucho.com>의 하위 엘리먼트

<log id='/log' href='stderr:' timestamp='[%Y-%m-%d %H:%M:%S.%s]'/>
로깅을 위한 설정부분이며 id별로 각기 다른 로그를 지정할 수 있다.
rollover-count : 몇번의 로그변경을 보관할지 설정
rollover-period : 로그변경 주기 설정
ex> rollover-period=’1d’라고 설정하면 stderr.log.20050301의 형태로 매일 로그가 쌓이게 된다

<security-provider id='com.sun.net.ssl.internal.ssl.Provider'/>
JSSE(Java Secure Socket Extension)를 사용하기 위한 security-provider설정 부분으로 Caucho에서는 설정의 어려움과 성능 저하를 이유로 OpenSSL이나 SSL for Apache/IIS를 사용할 것을 권고하고 있으므로 특별한 경우가 아니면 사용하지 않는 것이 좋다.

<java compiler="internal" compiler-args=""/>
Compiler옵션으로 internal이 default이며 javac나 jikes등의 외부 컴파일러를 지정할 수 있다.

<resource-ref>
<res-ref-name>jdbc/test</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<init-param driver-name="com.caucho.jdbc.mysql.Driver"/>
<init-param url="jdbc:mysql_caucho://localhost:3306/test"/>
<init-param user=""/><init-param password=""/>
<init-param max-connections="20"/>
<init-param max-idle-time="30"/>
</resource-ref>

JNDI를 사용한 resin connection pool을 사용할 경우 설정이며 res-ref-name 은 pool name을 의미하며 그 외의 설정은 tag와 property를 참조한다.

<jsp precompile='true' static-encoding='true' recompile-on-error='true'/>
jsp페이지를 precompile하도록 지정하여 다음 접속시 바로 이용할 수 있도록 한다.
Recompile-on-error를 지정하여 jsp페이지를 compile시 java.lang.error가 발생하면 자동으로 recompile되도록 한다. Shift-JIS와 같은 인코딩을 이용해서 writing하고 UTF-8으로 printing하려면 static-encoding을 비활성시킨다.


2. <http-server>의 하위 엘리먼트

<doc-dir>doc</doc-dir>
Server의 root file이 위치한 디렉토리를 지정한다.
Apache : /usr/local/apache/htdocs, IIS : d:inetpubwwwroot로 변경하여 사용할 수 있다.

<http port='8080'/>
Resin을 Standalone으로 구동할 경우 사용할 http port를 지정한다.

<srun host='127.0.0.1' port='6802'/>
Load balancing을 위해 Apache나 IIS에 plugin으로 Resin을 사용할 경우의 srun port를 지정한다.

<http port=8443>
<ssl>true</ssl>
<key-store-type>pkcs12</key-store-type>
<key-store-file>keys/server_cert.p12</key-store-file>
<key-store-password>changeit</key-store-password>
</http>

SSL을 사용하는 경우 위와 같은 설정을 하며 아래의 내용을 포함한다.
key-store-type : key store의 종류(ex:비밀키, 공개키 방식)
key-store-file : key store 파일의 위치
key-strore-password : key store파일 access password

<user-name>resin</user-name>
<group-name>resin</group-name>

만약 Unix에서 bin/httpd.sh을 root로 구동할 경우 web server user를 위해 특정 Group name과 User name을 제공해야 한다. RedHat 9.0이후 Linux에서는 bin/resin을 사용해야 한다.

<class-update-interval>2s</class-update-interval>
사용자에 의해 class파일이 update됬는지 여부를 검사하는 주기이다. 이 주기에 따라 resin이 검사하므로 해당 주기를 줄이면 변경즉시 반영되므로 개발시 유용하나 서버에 부하가 가중되므로 운영시는 낮춰주는 것이 좋다.

<servlet-classloader-hack>false</servlet-classloader-hack>
True로 설정하면 Servlet spec 에 따른 classloader를 사용하고 false이면 JDK spec에 따른 classloader를 사용한다.

<error-page exception='connection' location='/my-error-page.html'/>Apache나 IIS를 이용할 시에 srun connection 실패시 표시할 error 페이지를 지정한다.

<caucho-status>true</caucho-status>
Caucho-status를 표시한다.

<thread-max>150</thread-max>
서버당 최대 thread갯수

<thread-keepalive id='100'/>
Keepalive할 trhead수.

<request-timeout id='65s'/>
Socket keepalive 시간. SSL을 사용하지 않는다면 짧게 하는 것이 성능에 좋다.

<accept-buffer-size id='256'/>
Accept buffer상의 socket 수

<thread-min id='5'/>
Socket에서 대기될 때 요청되는 최대 thread 수

<ping sleep-time='1m' retry-count='3' retry-time='1s'>
<url>http://localhost:8080/ping/ping.jsp</url>
</ping>

web server 상태를 확인하기 위해 ping test를 하기 위한 설정이다.
test결과 failuer가 발생하면 resin은 서버를 restart한다.
해당 test를 위한 thread가 JDK와 같기 때문에 해당 Thread의 deadlocks이나 Database connection 증가로 인한 memory leak같은 경우는 catch할 수 없다.

<ignore-client-disconnect>true</ignore-client-disconnect>
Browser를 disconnect할 때 발생하는 ClientDisconnectionExceptions을 무시한다

<cache dir='cache' size='1024' entries='8192'/>
internal caching을 가능하게 한다.
Dir : cache가 적재될 directory
size : memory size(kb)
entries : entry의 전체 개수

<cache-mapping url-pattern='/' expires='2s'/>
Cache된 파일의 유효시간 일반적으로 운영중에는 15m으로 설정한다.

<servlet-mapping url-pattern='*.xtp' servlet-name='xtp'/>
<servlet-mapping url-pattern='*.jsp' servlet-name='jsp'/>

<host>외부의 servlet 설정이다. 이곳에 위치하면 모든 host에 적용된다.

<servlet-mapping url-pattern='/servlet/*' servlet-name='invoker'/>
URL로부터 servlet명으로 invoke한다. 예를 들어 /examples/basic/servlet/HelloServlet 은 HelloServlet class로부터 시작한다. 일반적으로, 보안상 헛점이 생길 수 있으므로 개발중에만 사용하며 운영중에는 사용하지 않는다.

<welcome-file-list>index.xtp, index.jsp, index.html</welcome-file-list>
시작페이지를 지정한다.


3. <host>의 하위 엘리먼트

<access-log id='logs/access.log'
format='%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
rollover-period='1W'/>
<error-log id='logs/error.log'/>

Resin 단독구동 시 로깅을 위한 내용이다.

<war-dir id='webapps'/>
Specifies a special directory to place war file이 위치한 특정 directory를 지정한다.
War directory에 위치한 어떤 .war (web-application resource) file이던지 자동으로 확장/설치 된다. 일반적으로, war file은 설정을 위해 WEB-INF/web.xml 을 사용하며 구문은 다른 application 설정과 동일하다.


4. <web-app>의 하위 엘리먼트

<classpath id='WEB-INF/classes'
source='WEB-INF/classes'
compile='true'/>

classpath를 지정한다. Source는 option이다. Servlets 과 beans는 일반적으로 WEB-INF/classes에 위치한다.

<session-config>
<session-max>4096</session-max>
<session-timeout>30</session-timeout>
<enable-cookies>true</enable-cookies>
<enable-url-rewriting>true</enable-url-rewriting>
<file-store>WEB-INF/sessions</file-store>
</session-config>

세션에 관한 설정이다.
Session-max : 최대 session 개수
Session-timeout : session timeout 시간(초)
Enable-cokkies : 쿠키 사용여부
Enable-url-rewritin :
세션을 아래의 방법중 하나를 사용하여 외부에 적재할 경우 개발 중 class 변경으로 인한 세션 삭제를 방지할 수 있다.
File-store : 세션을 지정된 디렉토리에 적재한다.
tcp-store : tcp-ring을 사용하여 세션을 적재한다.
Jdbc-store : DB table을 사용하여 세션을 적재한다.

<multipart-form upload-max='-1'/>
multipart-mime/form 사용을 가능하게 한다.

<path-mapping url-regexp='^/~([^/]*)' real-path='/home/$1/public_html/'/>
url path를 실제 path에 mapping한다.

<context-param info='An application information string'/>
Application init parameter이다.

<servlet-mapping url-pattern='/snoop/*' servlet-name='snoop-servlet'/>
모든 url에서 /snoop에 대하여 snoop-servlet 이름으로 접근한다.

<servlet servlet-name='snoop-servlet' servlet-class='Env'>
<init-param info='A servlet information string'/>
</servlet>

snoop-servlet init parameter로 info='A servlet information string'를 사용하여 구동한다.
by 제이 | 2005/04/06 00:28 | JAVA, JSP 썅!! | 트랙백(81) | 덧글(0)
가비지콜렉션

가비지 콜렉션


2004년 1월의 테크팁이었던 href="http://java.sun.com/developer/JDCTechTips/2004/tt0122.html#2">Monitoring
Class Loading and Garbage Collection
에서는 자바 커맨드 라인 툴 (자바 애플리케이션 런쳐)를 이용하여 커맨드 라인 옵션인 -verbose:gc에 대해서 알아보았다. 표준이 아니라는 이유로 -XX 옵션에 대해 신경 쓰지 않는다거나, 옵션의 균질성을 중요시한다거나, 또는 현재 사용하고 있는 옵션에 대해서 알고 싶다면 다음과 같이 -verbose:gc 를 입력해보자.

     -XX:+PrintGC -XX:+TraceClassUnloading

이것은 -verbose:gc 옵션이 어떻게 번역되는지를 보여준다.


이 옵션은 애플리케이션이 실행되는 동안 가비지 콜렉션 이벤트의 리포트를 요구한다. J2SE v1.4.2에서는 가비지 콜렉터를 컨트롤하는 다른 많은 커맨드 라인이 있다. 코드의 어느 라인도 변경하지 않고 애플리케이션의 성능을 최대한으로 활용하려면, 이 옵션들을 최소한 하나 이상 사용할 수가 있다. 이번 팁에서는 가비지 콜렉터를 컨트롤하는 부수적인 많은 자바 커맨드 라인을 다룰 것이지만, 가비지 콜렉션 튜닝 옵션의 전체를 말하지는 않는다.


자바 애플리케이션 런쳐는 표준 커맨드-라인 스위치와 비표준 커맨드-라인 스위치를 모두 동반한다. -verbose:gc 옵션에 관한 정보에 대해 알아보려면, 특정 플랫폼별로 다음 웹페이지를 참고하자.



위의 참고 페이지에는 몇몇 비표준 옵션들에 대해서도 나와있다. 자바 커맨드 라인 툴에서 표준 옵션인 -X을 실행하면 사용자의 플랫폼을 위한 비표준 옵션을 보여준다.


솔라리스 플랫폼이라면, 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
-Xrunhprof[:help]|[:<option>=<value>, ...]
perform JVMPI heap, cpu, or monitor profiling
-Xdebug enable remote debugging
-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

The -X options are non-standard and subject to change without
notice.

위의 출력값의 마지막 줄을 주목하자. 그러한 비표준 옵션들을 사용하는 것은 사용자의 책임 하에 있음을 분명히 나타내고 있다.


가비지 콜렉션을 위한 세 가지 특정한 비표준 옵션은 -Xnoclassgc, -Xincgc,
그리고 -Xloggc:<file>이다. -Xnoclassgc 옵션을 지정한다면, 콜렉션이 발생하지만 클래스들은 permanent generation으로부터 수집되지 않는다. 이는 콜렉션이 수거하는 메모리가 없음을 의미한다. 만약 클래스를 로딩하려고 할 때 이미 로딩된 클래스들을 위해 메모리를 써버렸다면 콜렉션은 문제를 해결하지 않을 것이다.


썬의 1.4.2 구현에서 -Xincgc은 -XX:+UseTrainGC 옵션과 동등한 값을 가진다. 즉, -Xincgc은 old generation을 위해서 디폴트 값에서 주어진 "serial" 콜렉터를 사용하기 보다는 "train"콜렉터를 사용한다. ("generations"의 개념은 이번 팁의 후반에서 다루게된다.) train 콜렉터는 약 10%의 퍼포먼스 부하를 발생시킨다. 또한 old generation 전체를 한 번에 수집할 수 없기 때문에 약간의 공간적인 부하(space overhead)도 발생한다. train 콜렉터는 점진적으로 작동하는 장점을 가지기 때문에 휴지 시간이 상당히 짧다. J2SE 1.5.0 베타 릴리즈에서는 -Xincgc 플래그는 train 콜렉터보다는 CMS(concurrent mark sweep) 콜렉터를 인보킹하게 된다. 그 이유는, CMS 콜렉터가 train 콜렉터보다 더 균일하기 때문이다.


-Xloggc 옵션을 지정해줌으로써 -verbose:gc 출력값의 등가를 파일로 출력할 수 있다. -verbose:gc 에 의한 출력값과 비교해 보면, -Xloggc 에 의한 출력값은 한 가지 추가적인 항목을 포함하는데, 그것은 애플리케이션에서의 첫 콜렉션이 일어난 시간에서부터 가비지 콜렉션이 발생한 시간에 대한 정보이다. 다음의 -Xloggc 샘플 출력값에서 살펴보자.

   0.000: [GC 512K->153K(1984K), 0.0198483 secs]
0.372: [GC 506K->281K(1984K), 0.0206428 secs]
0.393: [Full GC 281K->281K(1984K), 0.0888926 secs]
0.519: [GC 947K->941K(1984K), 0.0045715 secs]
0.524: [Full GC 941K->941K(1984K), 0.0797666 secs]
0.650: [GC 2107K->1597K(2808K), 0.0013546 secs]
0.838: [GC 2107K->1700K(2808K), 0.0116557 secs

파일에 재전송할 필요없이 -XX:+PrintGCTimeStamps 옵션을 지정해줌으로써 -Xloggc 출력값에 시간 소인을 나타낼 수 있다.


-Xnoclassgc, -Xincgc 과 -Xloggc:<file> 이외에도 가비지 콜렉터를 컨트롤하는 다른 옵션들이 존재한다. 예를 들자면, 콜렉터가 실행할 때 간접적으로 영향을 끼치도록 -Xms와 -Xmx 옵션을 사용하여 메모리 할당의 풀 사이즈를 변경할 수 있다. -Xms의 값을 크게 지정해주면, 그만큼 큰 자바 객체 힙으로 작업하게 된다. 이것은 힙을 채우는 데에 더 많은 시간이 걸린다는 것을 의미하고 결과적으로 콜렉션을 피하지는 않지만 미루게 된다는 것이다. 큰 -Xms 값은 또한 더 많은 시스템 리소스를 소모하게 된다. 반면 -Xmx의 값을 크게 지정하면, 자바 객체 힙의 사이즈가 필요 시에 커질 수 있게 된다. 이렇게 값이 큰 객체 힙은 조금 덜 빈번하게 가비지 콜렉트된다는 점 외에는 모든 점이 동일하다. 따라서 이 옵션을 통해 가비지 콜렉션이 빈번하지만 짧은 시간동안 일어나는 것과, 횟수는 적지만 오랜 시간동안 가비지 콜렉션이 일어나는 것 중 하나를 선택할 수 있다. 하지만 각각의 콜렉션이 부하를 발생하기 때문에, 사용자가 정의하는 요구조건에 따라 더 나은 접근법은 달라지게 되어있다.


-Xms 와 -Xmx 외에도 가비지 콜렉션에 영향을 주는 비표준 옵션들이 존재한다. 그 옵션들은 java -X 도움말에서 볼 수 없는데, 그 이유는 이 옵션들은 비표준 옵션이기 때문에 두개의 X를 사용해야하기 때문이다.


만약 힙의 최소값과 최대값을 지정해주고자 한다면 (이 때, 디폴트 값의 범위는 40%~70%),
-XX:MinHeapFreeRatio=<Minimum> 옵션과 -XX:MaxHeapFreeRatio=<Maximum> 옵션을 사용하면 된다. 그러나 최소값을 너무 작게 설정해놓으면, 힙은 콜렉션 후에 충분한 여유공간을 확보하지 못하게 되므로, 단기간 내에 또다시 콜렉팅을 해야할 수도 있다. 반면, 최소값을 크게 설정하면, 더 많은 "head room"이 생기므로 다음 콜렉션 때까지 시간을 지연시킬 수 있을 것이다. -XX:MinHeapFreeRatio 의 값을 크게 했을 때의 단점은 시스템 메모리가 이 가상 장치에 고정되기 때문에 사용자의 또 다른 애플리케이션에 사용될 수 없다는 점이다.


-XX 세트내의 다른 비표준 옵션들은 가비지 콜렉터의 작동 방법에 영향을 끼친다. -XX:+UseConcMarkSweepGC 커맨드 라인 플래그("concurrent mark sweep garbage collection"의 줄임말)는 병행(concurrent) 가비지 콜렉터를 실행시킨다. -XX:+UseConcMarkSweepGC 콜렉터는 old generation을 동시에 콜렉팅한다. 그 이유는 전반적인 콜렉션 같은 old generation 들은 콜랙팅할 때 대체적으로 시간이 오래 걸려서 병행 가비지 콜렉터를 실행시키는 것이 더 효과적이기 때문이다. 이는 애플리케이션에서 가비지 콜렉션 휴지 시간을 짧게 할 수 있도록 프로세싱 파워를 활용한다는 것을 말한다. -XX:+UseParallelGC 커맨드 라인 플래그는 병렬 가비지 콜렉팅을 가능하게 한다. -XX:+UseParallelGC 콜렉터는 오직 다중 프로세서를 이용한 young generation만을 콜렉팅한다. 이 접근법은 처리율은 높이지만, 전반적인 콜렉션의 휴지 시간을 줄이지는 못한다.


또 하나의 흥미로운 옵션인 -XX:+PrintGCDetails 는 각각의 콜렉션에서 각 generation에 어떤 일이 발생했는지를 보여준다. (단, permanent generation 에 대한 자세한 사항들은 보여주지 않는다. 이 점은 JDK 1.5.0.에서 수정되었다.)


SwingSet2의 데모에 사용된 -XX:+PrintGCDetails의 예제를 살펴보자. (출력값의 라인들은 테크팁 페이지의 경계선에 맞추기 위해 나누어주었다.)

   java -Xloggc:details.out -XX:+PrintGCDetails -jar
/home/eo86671/j2sdk1.4.2/demo/jfc/SwingSet2/SwingSet2.jar

0.000: [GC 0.000: [DefNew: 511K->64K(576K), 0.0182344 secs]
511K->153K(1984K), 0.0185255 secs]
1.387: [GC 1.387: [DefNew: 417K->64K(576K), 0.0192086 secs]
1.407: [Tenured: 217K->281K(1408K), 0.0725645 secs]
506K->281K(1984K), 0.0923346 secs]
1.559: [GC 1.559: [DefNew: 10K->3K(576K), 0.0044098 secs]
1.564: [Tenured: 937K->941K(1408K), 0.0741569 secs]
948K->941K(1984K), 0.0790573 secs]
1.703: [GC 1.703: [DefNew: 510K->0K(576K), 0.0011627 secs]
2107K->1597K(2808K), 0.0013820 secs]
2.210: [GC 2.210: [DefNew: 509K->64K(576K), 0.0112637 secs]
2107K->1710K(2808K), 0.0115942 secs]
2.927: [GC 2.927: [DefNew: 575K->64K(576K), 0.0170128 secs]
2222K->1841K(2808K), 0.0173293 secs]
8.430: [GC 8.430: [DefNew: 576K->64K(576K), 0.0142839 secs]
2353K->2025K(2808K), 0.0156266 secs]
8.823: [GC 8.823: [DefNew: 494K->64K(576K), 0.0164915 secs]
2456K->2243K(2808K), 0.0166323 secs]
8.856: [GC 8.856: [DefNew: 569K->41K(576K), 0.0058505 secs]
8.862: [Tenured: 2341K->1656K(2360K), 0.1464764 secs]
2749K->1656K(2936K), 0.1526133 secs]

변경내용이 그 결과에 어떤 영향 (예를 들어서, 콜렉션간의 시간 연장이나 각각 특정 휴지 시간의 감소)을 주는지를 알아보기 위해 -Xms 옵션과 -Xmx 옵션을 조정해보자.


앞서 'generations'의 힙 공간에 대해 언급한 바 있다. 이 말이 무슨 의미인지 궁금하다면, 여기 짤막한 설명을 참조하자. JRE 1.4.2 에서 힙은 young, old, 그리고 permanet와 같이 세 개의 generation으로 구분된다. young generation은 객체가 생성된 장소이다. 이 generation의 사이즈는 -XX:NewSize 옵션과 -XX:MaxNewSize 옵션에 의해 컨트롤된다. 새로운 객체를 보유하게 되면, old generation으로 승격된다. old generation의 사이즈는 전체 힙 사이즈 (-Xms 과 -Xmx) 에서 young generation의 사이즈 (-XX:NewSize 과 -XX:MaxNewSize)를 감한 사이즈로 계산된다. young generation은 명시적 크기보다는 -XX:NewRatio=를 이용하여 더 잘 지정할 수 있는데, -XX:NewRatio= 옵션은 힙 설정의 전체 사이즈를 설정해주기 때문이다. 선택한 사이즈는 콜렉팅 되는 시간에 대해 콜렉션의 횟수를 결정하게 된다. 어떤 애플리케이션들은 짧은 휴지 시간을 요구하는 한편, 콜렉터의 효율성을 필요로 하는 애플리케이션들도 있다. (많은 애플리케이션에서 이 단계의 튜닝을 해줄 필요는 전혀 없다.)


마지막으로 언급할 GC옵션은 -XX:+DisableExplicitGC이다. System.gc()의 호출을 이용하여 가비지 콜렉터를 실행하라는 명령을 했을 때, 시스템이 이를 무시하기를 원한다면 이 옵션을 사용해보자. 가비지 콜렉터는 프로그래머가 명령했을 때가 아닌, 필요시에 계속해서 실행되고 있을 것이다. 하지만 프로그래머의 명령을 무시하는 것이 좋은 생각일까? 아마도 아닐 것이다. 왜냐하면, 프로그래머가 가비지 콜렉터를 실행하기 적당한 시점이라고 생각할 수 있기 때문이다. 물론, 만약 공유 라이브러리를 사용하고 있고, 실행 콘텍스트가 프로그래머의 원래의 계획과 다르다면, 이러한 새로운 상황에 유효하지 않을 수도 있다.


가비지 콜렉션에 관련된 옵션들에 대해 좀더 자세한 정보를 원한다면, href="http://java.sun.com/docs/hotspot/gc1.4.2/">Tuning
Garbage Collection with the 1.4.2 Java Virtual Machine
를 읽어보기 바란다. 튜닝을 할 때는 튜닝의 목적이 성능 향상인지, 짧은 휴지 시간인지 아니면 작은 범위의 ㄴ메모리 사용을 위한 것인지 그 목적을 확실히 해야 한다. JVM이 업데이트 되면서 선택할 수 있는 튜닝 옵션들도 다시 한번 살펴 보아야 한다. 이번 팁에서 다룬 옵션들의 대부분은 썬의 런타임 환경 1.4.2 릴리즈에 한정됨을 기억하자. 1.5.0 릴리즈는 고유의 컨트롤 셋과 디폴트 셋을 가진다.

by 제이 | 2005/03/31 11:27 | JAVA, JSP 썅!! | 트랙백 | 덧글(0)


<< 이전 페이지 다음 페이지 >>