반응형

소스 혹... 잊어 버릴까봐

첨부함.

반응형

'Language > JSP' 카테고리의 다른 글

로딩된 CLASS 정보 보기  (0) 2011.03.10
JSP WEB shell  (0) 2010.05.10
JSP 파일 읽기  (0) 2009.12.03
웹 상에서 바로 엑셀로 저장하기  (0) 2009.03.06
반응형
반응형

'Language' 카테고리의 다른 글

npm node module 공통 관리하기  (0) 2020.10.28
Inter-process communication  (0) 2014.03.27
특정 문자열 분석 쉘  (0) 2012.02.21
빠른 수식 계산법  (0) 2010.03.01
반응형



<script type="text/javascript">
//function decimalToHex(int number)
//{
// 10진수 123을 16진수로 변환
//var n = (123).toString(16);
//document.write(n, '<br />');
// 출력 결과: 7b
//}

// 그냥 배열을 통해 값 출력
function getRGB(number)
{

 var rgb_list = new Array();
 rgb_list[0] = '616264';
 rgb_list[1] = 'D8C8B2';
 rgb_list[2] = 'B82647';
 rgb_list[3] = '683235';
 rgb_list[4] = 'C23352';
 rgb_list[5] = 'BF2F7B';
 rgb_list[6] = 'BE577B';
 rgb_list[7] = 'F9D537';
 rgb_list[8] = 'F7B938';
 rgb_list[9] = 'DDA28F';
 
 document.myform.change_value11.value = rgb_list[number];
 document.all.test2.style.background = rgb_list[number];
 // 참고 사이트
 //http://july.tistory.com/290
 // http://parangsaekr.com.ne.kr/rgb01.html
}

// 대문자 헥사로 출력
function decimalToHex(number)
{
 var my_number1 = (parseInt(number) * 10) % 256;
 var my_number2 = (parseInt(number) * 20) % 256;
 var my_number3 = (parseInt(number) * 30) % 256;
 
 document.myform.change_value11.value = my_number1 + '_' + my_number2 + '_' + my_number3;
 //alert(my_number1);
 
 var rgb_r = my_number1.toString(16).toUpperCase();
 var rgb_g = my_number2.toString(16).toUpperCase();
 var rgb_b = my_number3.toString(16).toUpperCase();
 
 alert(rgb_r.length);
 
 // 2자리로 보정 처리
 if (rgb_r.length == 1) rgb_r = '0' + rgb_r;
 if (rgb_g.length == 1) rgb_g = '0' + rgb_g;
 if (rgb_b.length == 1) rgb_b = '0' + rgb_b;
 
 document.myform.change_value.value = rgb_r + '_' + rgb_g + '_' + rgb_b;
 document.all.test2.style.background=rgb_r + rgb_g + rgb_b;
 
 //n = (123).toString(16).toUpperCase();
 //document.write(n, '<br />');
// 출력 결과: 7B
}
</script>


<html>
<body>
<form name="myform">
<input type=text value = "" name="mynumber">
<input type=button value="확인" onclick="decimalToHex(document.myform.mynumber.value)">
<input type=button value="지정된 번호 확인" onclick="getRGB(document.myform.mynumber.value)">
<br>
<br>
div !
<br>
<div id=test2 style="position:absoulte; left:100; top:100; width:150; height:50">
 테스트 레이어
</div>
<input type=text value="" name="change_value11"><BR>
<input type=text value="" name="change_value">
</form>
</body>
</html>

반응형
반응형

log4sql은 많이 알려지진 않았지만 상당히 유용한 sql 로거입니다. 만든 분은 송인섭이라는분인데 한국분이신것 같습니다.

 기존에 우리는 sql을 디버깅 하기 위해서 대부분 System.out.println()을 사용했습니다. log4j를 이용해서 sql을 콘솔에 찍기도 하고 jdbc관련 클래스를 만들어서 사용하는 경우에도 결국엔 System.out.println()을 사용하게 됩니다. 하지만 sql을 이렇게 콘솔로 표현하게 되면 여러가지 불편한 점들이 생깁니다.
 가장 큰 문제점이 바로 소스는 소스대로, 콘솔은 콘솔대로 가독성이 떨어지게 됩니다. 소스상에서 System.out을 사용하게되면 개발이 끝나도 운영에 들어가게되면 이 부분을 주석처리하거나 다른 설정을 하게됩니다.
 또한 콘솔에서 보여질때 sql을 소스상에서 들여쓰기를 해주지 않았다면 단순한 sql은 괜찮지만 복잡한 sql은 볼때마다 매번 들여쓰기를 해주어야 한다는 단점이 있습니다. log4sql은 이러한 문제를 해결하기 위해서 간단히 드라이버 설정만 바꿔주면 System.out.println()을 사용하지 않아도, 소스상에서 들여쓰기를 해주지 않아도 콘솔에 예쁘게 들여쓰기가 된 sql을 보여줍니다.

log4sql이 뭔가요?

간단하게 log4sql이 무엇인지 백번 설명을 하기보다는 한번 보는게 확실히 이해하기 쉬울꺼 같습니다.
사용자 삽입 이미지
저 노란색 박스안에 나타난 내용이 log4sql에서 자동으로 찍어준 내용입니다. sql이 실행된 시간과 실행된 메소드, sql이 실행되는데 소요된 시간, 실행된 sql을 자동으로 들여쓰기를 해서 보여주고 있습니다.

log4sql의 장점

1. 자동 들여쓰기
일반적으로 콘솔에 들여쓰기가 된 sql을 보려면 다음과 같은 코드를 작성해야 했습니다.
1
2
3
4
StringBuffer  var11 = new StringBuffer();<BR>
var11.append("SELECT * \n");<BR>
var11.append(" FROM   emp \n");<BR>
var11.append(" WHERE  deptno = '30' \n");<BR>
하지만 더이상 이런 부분은 신경 쓰지않아도 됩니다. log4sql을 사용한다면 말이죠.

2. 페이지 반응속도가 느리다구요?
log4sql은 쿼리가 실행되는데 걸린 시간을 표시해줍니다. 어느 부분이 느린지 어느 쿼리가 느린지 콘솔을 보기만 하면 알 수 있습니다.

3. 이 쿼리가 어디서 날린 쿼리지?
log4sql은 쿼리가 실행된 메소드도 표시해 줍니다. 하지만 쿼리를 날리는 클래스를 별도로 두고 해당 클래스를 이용해서 쿼리를 날리게 되면 log4sql도 어쩔수 없답니다.^^;;

어떻게 사용하나요?

간단합니다. JDBC 관련 설정에서 드라이버 설정만 바꿔주면 됩니다. 예를 들어 오라클을 사용하는 경우 기존에 'oracle.jdbc.drirver.OracleDriver' 드라이버를 사용했다면 'core.log.jdbc.driver.OracleDriver'로 바꿔주기만 하면 됩니다. 이제 log4sql을 사용할 준비가 된겁니다. 이제 WAS를 올리고 콘솔을 한번 보세요. sql이 깔끔하게 정리되어서 나타날꺼에요.^^
다른 디비를 사용하시는 경우에는 log4sql 홈페이지에서 확인하시면 됩니다.

다음에는 log4sql의 상세 설정에 대해서도 알아보도록 하겠습니다.

반응형
반응형

개발환경에서 JAVA + ORACLE + JSP 일반적인 환경에서

ORACLE은 일반적으로 PL/SQL을 사용한다

그런데,,,

이 PL/SQL을 사용하다보면 JAVA + JSP를 통한 debugging 을 하기가 굉장히 어렵고

제대로 변수가 입력되었는지... 잘 동작하는지 찾기 어렵다.

그래서... 인터넷 검색을 하다보니

log4j 처럼

log4sql이라는 lib가 있다는 것을 알았다...

프로젝트 홈 : http://log4sql.sourceforge.net/index_kr.html

  Download log4sql-7.0.8



홈페이지에서 보면,

개발을 할경우 우리는 무수히 많은 프레임웍과 도구들을 사용 합니다.
View계층, Model계층 그리고 Controller또는 DataAcess계층에서 많은 도구들과 프레임웍은
현재 거의 모든 layer들에 걸쳐서 사용되고 개발되어지고 있습니다.
이런경우 개발자에게 중요한것중의 하나는 비즈니스 로직인 PL/SQL작업을 하는 것 입니다.
모든 PL/SQL작업은 SQL을 작성하고 실행한 후에 버그가 존재하가나 원하는 결과가 나오지 않을경우 수정하는 것이며
이런작업은 반복적으로 수행됩니다.
이런 경우에 우리는 jdbc또는 Connection Pooling(Apache DBCP) 또는 support 2PC (JTA,JOTM)등을 사용합니다.
또한 우리는 데이터 접근계층의 프레임웍으로 IBATIS나 SpringFramework을 사용할 경우 springJDBC또는 HIBERNATE등을 사용합니다.
이런 상황에서 우리는 의문을 갖게 됩니다. '내가 작성한 SQL이 정상적으로 작동한 것인가?', '내가 입력값으로 넣어준 값들이 제대로 등록된 SQL인가?'
SQL을 확인하고자 개발 소스의 구석구석에 System.out.println(...)으로 확인을 할 것입니다.
개발이 종료된 시점에 주석으로 가려진 가독성이 떨어지는 소스는 유지보수담당자에게 머리아픈 소스가 될 것이고
운영상에 문제가 발생할 경우 해당 SQL을 다시 확인하는 복잡한 작업이 반복될 것입니다.
이런경우를 경험한 개발자라면 log4sql은 간단한 설정으로 많은 편리함을, 개발의 즐거움을 당신에게 드릴것 입니다.

기본 apache common lang lib를 의존한다. : http://commons.apache.org/lang/

오픈 소스이며 apache 라이센스를 따른다 : http://www.apache.org/licenses/LICENSE-2.0





특징들

1.쉬운 설정.
log4sql은 굉장히 간단한 설정을 지원합니다.
개발자들이 할 일은 log4sql-xxx.jar파일을 클래스패스에 복사한 후에
사용하고 있는 driver-class 이름을 log4sql에서 지원하는 driver-class명으로만 바꿔주시면 됩니다.

 2.개발성 향상.
 log4sql은 SQL파라미터 매치된 로그뿐 아니라, 로그의 위치와 걸린시간 그리고 수행결과(INSERT,UPDATE,DELETE)를 보여드립니다.

 3. SQL실행시간을 가장 정확히 파악합니다.
log4sql은 실제 서비스와 동일한 시점의 순수한 SQL실행 시간만을 나타냅니다.
즉 이것은 Connection을 얻어오거나, Connection Pooling기법을 사용하더라도, 실제 작성된 SQL이 DBMS에 전달된 후 결과 값을 받아오기까지의 걸린 시간만을 측정하여
성능에 이상이 있는 SQL등을 쉽게 확인 할 수 있습니다.
 
 4. Application의 성능과 실행에 영향을 주지 않습니다.
 log4sql은 내부적인 문제가 존재하더라도 실제 Application에 영향을 주지는 않습니다.
내부적인 문제가 발생할 경우 해당로그는 설정변경으로 확인이 가능하며, 동시접속사용자가 많은경우 log4sql은 비동기 모드로의 전환이 실행환경에서 가능하도록 설계되었습니다.

5.어떤 유형의 프로젝트라도 적용이 가능합니다.
 JDK1.4이상의 아래의 JDBC드라이버 리스트를 사용하는 어떤 프로젝트라도 사용이 가능합니다.

[ORACLE DRIVER CLASS] 'oracle.jdbc.drirver.OracleDriver'
[MYSQL DRIVER CLASS] 'com.mysql.jdbc.Driver' or 'org.gjt.mm.mysql.Driver'
[SYBASE DRIVER CLASS] 'com.sybase.jdbc2.jdbc.SybDriver'
[DB2 DRIVER CLASS] 'com.ibm.db2.jcc.DB2Driver'
[INFOMIX DRIVER CLASS] 'com.informix.jdbc.IfxDriver'
[POSTGRESQL DRIVER CLASS] 'org.postgresql.Driver'
[MAXDB DRIVER CLASS] 'com.sap.dbtech.jdbc.DriverSapDB'
[FRONTBASE DRIVER CLASS] 'com.frontbase.jdbc.FBJDriver'
[HSQL DRIVER CLASS] 'org.hsqldb.jdbcDriver'
[POINTBASE DRIVER CLASS] 'com.pointbase.jdbc.jdbcUniversalDriver'
[MIMER DRIVER CLASS] 'com.mimer.jdbc.Driver'
[PERVASIVE DRIVER CLASS] 'com.pervasive.jdbc.v2.Driver'
[DAFFODILDB DRIVER CLASS] 'in.co.daffodil.db.jdbc.DaffodilDBDriver'
[JDATASTORE DRIVER CLASS] 'com.borland.datastore.jdbc.DataStoreDriver'
[CACHE DRIVER CLASS] 'com.intersys.jdbc.CacheDriver'
[DERBY DRIVER CLASS] 'org.apache.derby.jdbc.ClientDriver'
[ALTIBASE DRIVER CLASS] 'Altibase.jdbc.driver.AltibaseDriver'
[MCKOI DRIVER CLASS] 'com.mckoi.JDBCDriver'
[JSQL DRIVER CLASS] 'com.jnetdirect.jsql.JSQLDriver'
[JTURBO DRIVER CLASS] 'com.newatlanta.jturbo.driver.Driver'
[JTDS DRIVER CLASS] 'net.sourceforge.jtds.jdbc.Driver'
[INTERCLIENT DRIVER CLASS] 'interbase.interclient.Driver'
[PURE JAVA DRIVER CLASS] 'org.firebirdsql.jdbc.FBDriver'
[JDBC-ODBC DRIVER CLASS] 'sun.jdbc.odbc.JdbcOdbcDriver'
[MSSQL 2000 DRIVER CLASS] 'com.microsoft.jdbc.sqlserver.SQLServerDriver'
[MSSQL 2005 DRIVER CLASS] 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
[CUBRID DRIVER CLASS] 'cubrid.jdbc.driver.CUBRIDDriver'



사용법

 1.log4sql-xxx.jar파일을 클래스패스에 복사하십시요.
log4sql-xxx.zip파일은 sourceforge에서 다운로드한 후 압축을 해제합니다.
그리고나서 압축을 해제한 폴도에 가서 log4sql-xxx.jar을 복사한후 SQL로그를 보고자하는 어플리케이션을 lib디렉토리에 복사합니다.

 2.Driver Class이름을 변경합니다.
만약 Oracle Driver를 사용하신다면 driver-class의 설정은 아마도 아래와 같을 것입니다. <driver class='oracle.jdbc.driver.OracleDriver'>와 같다면 <driver class='core.log.jdbc.driver.OracleDriver'>로 변경하면 됩니다.
아래의 리스트는 log4sql에서 지원하고 있는 driver의 리스트 입니다.



JDBC TYPE Origin Your Driver Class -> log4sql Driver Class
[ORACLE DRIVER CLASS] oracle.jdbc.drirver.OracleDriver -> core.log.jdbc.driver.OracleDriver
[MYSQL DRIVER CLASS] com.mysql.jdbc.Driver' or'org.gjt.mm.mysql.Driver -> core.log.jdbc.driver.MysqlDriver
[SYBASE DRIVER CLASS] com.sybase.jdbc2.jdbc.SybDriver -> core.log.jdbc.driver.SybaseDriver
[DB2 DRIVER CLASS] com.ibm.db2.jcc.DB2Driver -> core.log.jdbc.driver.DB2Driver
[INFOMIX DRIVER CLASS] com.informix.jdbc.IfxDriver -> core.log.jdbc.driver.InfomixDriver
[POSTGRESQL DRIVER CLASS] org.postgresql.Driver -> core.log.jdbc.driver.PostgresqlDriver
[MAXDB DRIVER CLASS] com.sap.dbtech.jdbc.DriverSapDB -> core.log.jdbc.driver.MaxDBDriver
[FRONTBASE DRIVER CLASS] com.frontbase.jdbc.FBJDriver -> core.log.jdbc.driver.FrontBaseDriver
[HSQL DRIVER CLASS] org.hsqldb.jdbcDriver -> core.log.jdbc.driver.HSQLDriver
[POINTBASE DRIVER CLASS] com.pointbase.jdbc.jdbcUniversalDriver -> core.log.jdbc.driver.PointBaseDriver
[MIMER DRIVER CLASS] com.mimer.jdbc.Driver -> core.log.jdbc.driver.MimerDriver
[PERVASIVE DRIVER CLASS] com.pervasive.jdbc.v2.Driver -> core.log.jdbc.driver.PervasiveDriver
[DAFFODILDB DRIVER CLASS] in.co.daffodil.db.jdbc.DaffodilDBDriver -> core.log.jdbc.driver.DaffodiLDBDriver
[JDATASTORE DRIVER CLASS] com.borland.datastore.jdbc.DataStoreDriver -> core.log.jdbc.driver.JdataStoreDriver
[CACHE DRIVER CLASS] com.intersys.jdbc.CacheDriver -> core.log.jdbc.driver.CacheDriver
[DERBY DRIVER CLASS] org.apache.derby.jdbc.ClientDriver -> core.log.jdbc.driver.DerbyDriver
[ALTIBASE DRIVER CLASS] Altibase.jdbc.driver.AltibaseDriver -> core.log.jdbc.driver.AltibaseDriver
[MCKOI DRIVER CLASS] com.mckoi.JDBCDriver -> core.log.jdbc.driver.MckoiDriver
[JSQL DRIVER CLASS] com.jnetdirect.jsql.JSQLDriver -> core.log.jdbc.driver.JsqlDriver
[JTURBO DRIVER CLASS] com.newatlanta.jturbo.driver.Driver -> core.log.jdbc.driver.JturboDriver
[JTDS DRIVER CLASS] net.sourceforge.jtds.jdbc.Driver -> core.log.jdbc.driver.JTdsDriver
[INTERCLIENT DRIVER CLASS] interbase.interclient.Driver -> core.log.jdbc.driver.InterClientDriver
[PURE JAVA DRIVER CLASS] org.firebirdsql.jdbc.FBDriver -> core.log.jdbc.driver.PureJavaDriver
[JDBC-ODBC DRIVER CLASS] sun.jdbc.odbc.JdbcOdbcDriver -> core.log.jdbc.driver.JdbcOdbcDriver
[MSSQL 2000 DRIVER CLASS] com.microsoft.jdbc.sqlserver.SQLServerDriver -> core.log.jdbc.driver.MssqlDriver
[MSSQL 2005 DRIVER CLASS] com.microsoft.sqlserver.jdbc.SQLServerDriver -> core.log.jdbc.driver.Mssql2005Driver
[CUBRID DRIVER CLASS] cubrid.jdbc.driver.CUBRIDDriver -> core.log.jdbc.driver.CUBRIDDriver






설정하기

 우리는 log4sql_conf.jsp페이지를 통해서 보다 쉬운 운영상의 설정페이지를 제공합니다.
 
log4sql-xxx.zip파일을 sourceforge싸이트에서 다운로드한 후에 압축을 풀게되면 log4jsq_conf.jsp파일을 [log4sql-xxx.zip]/log4sql_conf.jsp에서 볼 수 있습니다.
log4sql_conf.jsp파일을 개발하고 있는 어플리케이션의 context-root에 복사하세요
(만약 개발중인 어플리케이션 서버의 context_root이름이 'site-root'이고 context_root까지의 디렉토리 구조가 'c://application/domain/site_root'와 같다면 log4sql_conf.jsp를 'c://application/domain/site_root/log4sql_conf.jsp'처럼 복사하세요)
그런 후에 log4sql_conf.jsp을 브라우져로 열기 위해서 'http://개발ip:개발port/site_root/log4sql_conf.jsp'처럼 접근합니다.

log4sql_sql.jsp의 형태는 아래와 같은 그림이 보여질것 입니다.


l환경설정에 사용되어지는 속성값들은 log4sql_configuration.properties에 위치하고 있으며,
항상 같은 설정이 적용되어지길 원할경우 [log4sql-xxx.jar]/core/log/log4sql_configuration.properties의 위치에서 해당항목의 값을 변경하시면 됩니다.


 1.Log Level(log4sql_configuration.properties 파일에서 'log.level'항목을 참조함).
Log Level은 로그를 남길경우의 우선 순위를 얘기하며, Log4j를 알고계신다면 쉽게 이해가 되실겁니다.
log4sql의 로그는 기본적으로 모두 DEBUG의 형태로 되어있으므로 DEBUG값 이상으로 로그레벨을 변경하게 되면 정상적인 로그를 남기지 않게 됩니다.
로그레벨의 종류는 DEBUG=0,INFO=1,WARNING=2,FATAL=3, ERROR=4, LOGGING_OFF=5와 같으며,
SQL수행도중에 구문문제 또는 잘못된 파라미터 INDEX에러가 발생하게 되면 자동으로 해당 SQL과 원인(Root cause Exception trace)를
ERROR 로그레벨로 남기게 되며, 모든 로그(Error 로그포함)를 남기지 않으려면 로그레벨을 LOGGING_OFF로 변경하시면 됩니다.

 2.Select Fix(log4sql_configuration.properties 파일에서 'query.logging.position.fixed.select'항목을 참조함).
 SELECT SQL에만 해당됩니다.
log4sql은 많은경우에 사용되어질 수 있도록 만들어 졌습니다.
따라서 우리는 log4sql을 사용하는 어플리케이션에서 어떠한 프레임웍을 사용하는지, 어떤 서버에서 실행되는지 알수가 없습니다.
문제가 있는 SQL일경우 사용자는 DEBUGING을 해야 할 것입니다. 그러기 위해선 어떤 클래서에서, 어떤 메소드에서 실행이 되었는지 알아야합니다.
log4sql은 SQL을 실행시킨 패키지와 클래스 그리고 어떤 메소드의 몇번째 라인에서 실행이 되었는지 실행된 시간과 함께 나타내 줍니다.
하지만, 위와같은 이유로 적합한 위치가 아닐수도 있으므로 log4sql은 적합한 위치를 사용자가 선택할 수 있도록 모든 실행에 참여한 클래스와 메소드들의 항목을 모두 보여줍니다.

 3.Select Position(log4sql_configuration.properties파일에서 'query.logging.position.select'항목을 참조함).
 2번 항목에서 적합하다고 생각되는 항목의 번호를 Select Position에 입력한 후 확인 버튼을 클릭하거나, 영구적으로 변경을 원하는 경우는 log4sql_configuration.properties에서 query.logging.position.select항목의 값으로 변경하시면 됩니다

 4.None Select Fix(log4sql_configuration.properties파일에서 'query.logging.position.fixed.none_select'항목을 참조함).
 INSERT, UPDATE, DELETE SQL에만 해당됩니다.
log4sql은 많은경우에 사용되어질 수 있도록 만들어 졌습니다.
따라서 우리는 log4sql을 사용하는 어플리케이션에서 어떠한 프레임웍을 사용하는지, 어떤 서버에서 실행되는지 알수가 없습니다.
문제가 있는 SQL일경우 사용자는 DEBUGING을 해야 할 것입니다. 그러기 위해선 어떤 클래서에서, 어떤 메소드에서 실행이 되었는지 알아야합니다.
log4sql은 SQL을 실행시킨 패키지와 클래스 그리고 어떤 메소드의 몇번째 라인에서 실행이 되었는지 실행된 시간과 함께 나타내 줍니다.
하지만, 위와같은 이유로 적합한 위치가 아닐수도 있으므로 log4sql은 적합한 위치를 사용자가 선택할 수 있도록 모든 실행에 참여한 클래스와 메소드들의 항목을 모두 보여줍니다

 5.None Select Position(log4sql_configuration.properties파일에서 'query.logging.position.none_select'항목을 참조함).
4번 항목에서 적합하다고 생각되는 항목의 번호를 Select Position에 입력한 후 확인 버튼을 클릭하거나, 영구적으로 변경을 원하는 경우는 log4sql_configuration.properties에서 query.logging.position.select항목의 값으로 변경하시면 됩니다.

 6.View Parameter Position(log4sql_configuration.properties파일에서 'query.logging.view.position'항목을 참조함).
 PreparedStatement를 사용할 경우 원하는 결과가 나오지 않을경우 해당 파리미터의 값이 적당하게 메치되었는지 궁금하게 됩니다.
이럴경우 개발자들은 난감한 상황에 접하게 됩니다. log4sql은 모든 파라미터가 결합된 SQL로그를 보여줍니다.
하지만, SQL문자열에 직접 입력된 값이 존재할 경우 개발자는 어떤값이 입력된 값인지, 어떤값이 문자열에 존재하는 값인지 혼동을 할 수 있습니다.
View Parameter Position값을 'true'로 변경할 경우 SQL log에서 입력된 값의 좌측에 '/**P*/'와 같은 표시를 보여줍니다. .

 7.View Internal Error(log4sql_configuration.properties파일에서 'query.logging.view.internal.exception'항목을 참조함).
 log4sql은 내부적인 문제가 있더라도 실제 SQL의 실행에는 영향을 미치지 않습니다.
log가 출력되지 않는 비정상적인 경우에 View Internal Error의 값을 'true'로 변경할 경우 내부적인 오류라면 오류의 내용을 출력할 것입니다.
이 내용은 사용자에게 고맙지 않은 경우일 것이나, 해당 내용을 log4sql개발팀에게 보내준다면 보다 견고한 버젼으로의 변경이 용이할 것입니다.

 8.Log To Asynchronous(log4sql_configuration.properties파일에서 'query.logging.asynchronous'항목을 참조함).
 log4sql은 SQL로그의 출력에 있어서 두가지 방법을 사용합니다. 동기적인 경우(Log To Asynchronous의 값을 'false'로 변경할 경우)모든 SQL로그는 순차적으로 실행이 될 것입니다.
이경우 log4sql에서의 모든 작업이 수행된 후에 다음 작업을 수행 할 것입니다.(많은 동시접속자가 존재하는 많은 로그가 출력되고 있는 상황에서 성능에 영향을 줄 수가 있습니다.
하지만 개발시 소수의 개발자에 의해서 디버그 용도로 실행되고 있을경우는 거의 영향을 주지 않습니다.) 비동기적인 경우(Log To Asynchronous의 값을 'true'로 변경할 경우)모든 SQL로그는 항상 순차적으로 실행되지 않습니다.
이경우 log4sql에서의 모든 작업과 SQL로그를 남기는 작업은 별도의 Thread로 동작할 것입니다.(많은 동시접속자가 존재(300tps)하는 많은 로그가 출력되고 있는 상황에서 성능을
보장하여 줄 것이나 이경우 로그는 순차적으로 남기지 않습니다.)

 9.View Appointed Package(log4sql_configuration.properties파일에서 'query.logging.view.appoint'항목을 참조함).
 아마도 개발자 혼자서 모든 개발을 진행하는 일은 흔치 않을것입니다.
따라서 log4sql은 개발자가 개발하고 있는 패키지 혹은 클래스 혹은 메소드에 한해서만 로그를 남길수 있습니다.
설정의 방법은 아래의 내용을 참조하세요.

[예약된 기호들]*..* : 모든 패키지를 의미합니다, * : 모든 클래스를 의미합니다, (..) : 모든 메소드들을 의미합니다
View Appointed Package의 값을 *..*.*.(..)로 입력한다면 실행 되고있는 모든 SQL로그가 보여질 것 입니다.
parttern 1 : *..*.*.(..) (모든 로그가 보여집니다.)
parttern 2 : net.*..*.*.(..) ('net'으로 시작되는 패키지 하위의 SQL모든 클래스의 로그가 출력됩니다.)
parttern 3 : *..*.Package_name.*..*.*.(..) ('Package_name'이 중간에 포함된 패키지의 로그가 보여집니다.)
parttern 4 : *..*.Package_name.*.(..)('Package_name'으로끝나는 모든 패키지의 로그가 보여집니다.)
parttern 5 : *..*.Class_name.(..) (클래스 이름이 'Class_name'인 곳의 로그가 보여집니다.)
parttern 6 : *..*.Package_name.Class_name.(..) (패지지가 'Package_name'로 종료되고 클래스 이름이 'Class_name'인 곳의 SQL 로그가 출력됩니다.)
parttern 7 : *..*.Package_name.*..*.Class_name.(..) (패키지가 'Package_name'을 포함하고 있고 클래스 이름이 'Class_name'인 곳의 SQL 로그가 출력됩니다.)
parttern 8 : *..*.Package_name.Class_name.Method_name (패키지의 이름이 'Package_name'로 종료되며, 클래스의 이름이 'Class_name'이고 메소드의 이름이 'Method_name'인 곳의 SQL 로그가 출력됩니다.)
parttern 9 : *..*.Package_name.*..*.Class_name.Method_name (패키지의 이름이 'Package_name'을 포함하고 있고 클래스 이름이 'Class_name'이고 메소드 이름이 'Method_name'인 곳의 SQL 로그가 출력됩니다.)
parttern 10 : *..*.Class_name.Method_name (모든 패키지에서 클래스 이름이 'Class_name'이고 메소드 이름이 'Method_name'인 곳의 SQL 로그가 출력됩니다.)
parttern 11 : *..*.Package_name.*.Method_name (패키지 이름이 'Package_name'로 종료되고 메소드 이름이 'Method_name'인 곳의 SQL 로그가 출력됩니다.)
parttern 12 : *..*.Package_name.*..*.*.Method_name (패키지 이름에서 'Package_name'를 포함하고 있고 메소드 이름이 'Method_name'인 곳의 SQL 로그가 출력됩니다.)
parttern 13 : *..*.*.Method_name (모든 패키지와 모든 클래스 중에서 메소드 이름이 'Method_name'인 곳의 SQL모든 로그가 출력됩니다.)
parttern 14 : net.sourceforge.log4sql.Class_name.Method_name (패키지가 'net.sourceforge.log4sql'이고 클래스 이름이 'Class_name'이고 메소드 이름이 'Method_name'인 곳의 SQL로그가 출력됩니다.)


p.$. : 아직 사용해 보진 않았다... 사용해야 하는데... 우선 간략하게 정리해본다... 쿠쿠... 시간나면 더 추가 ㄱ ~



반응형
반응형

TMAX 홈페이지에서 얻어왔음..


반응형

'Language > JSP' 카테고리의 다른 글

JSP 2.0 프로그래밍 소스  (0) 2011.10.12
JSP WEB shell  (0) 2010.05.10
JSP 파일 읽기  (0) 2009.12.03
웹 상에서 바로 엑셀로 저장하기  (0) 2009.03.06
반응형

출처(Reference) : http://www.javafaq.nu/java-example-code-141.html


// -----------------------------------------------------------------------------
// CLOBFileExample.java
// -----------------------------------------------------------------------------

/*
 * =============================================================================
 * Copyright (c) 1998-2005 Jeffrey M. Hunter. All rights reserved.
 *
 * All source code and material located at the Internet address of
 * http://www.idevelopment.info is the copyright of Jeffrey M. Hunter, 2005 and
 * is protected under copyright laws of the United States. This source code may
 * not be hosted on any other site without my express, prior, written
 * permission. Application to host any of the material elsewhere can be made by
 * contacting me at jhunter@idevelopment.info.
 *
 * I have made every effort and taken great care in making sure that the source
 * code and other content included on my web site is technically accurate, but I
 * disclaim any and all responsibility for any loss, damage or destruction of
 * data or any other property which may arise from relying on it. I will in no
 * case be liable for any monetary damages arising from such loss, damage or
 * destruction.
 *
 * As with any code, ensure to test this code in a development environment
 * before attempting to run it in production.
 * =============================================================================
 */
 
import java.sql.*;
import java.io.*;
import java.util.*;

// Needed since we will be using Oracle's CLOB, part of Oracle's JDBC extended
// classes. Keep in mind that we could have included Java's JDBC interfaces
// java.sql.Clob which Oracle does implement. The oracle.sql.CLOB class
// provided by Oracle does offer better performance and functionality.
import oracle.sql.*;

// Needed for Oracle JDBC Extended Classes
import oracle.jdbc.*;


/**
 * -----------------------------------------------------------------------------
 * Used to test the functionality of how to load and unload text data from an
 * Oracle CLOB.
 *
 * This example uses an Oracle table with the following definition:
 *
 *      CREATE TABLE test_clob (
 *            id               NUMBER(15)
 *          , do*****ent_name    VARCHAR2(1000)
 *          , xml_do*****ent     CLOB
 *          , timestamp        DATE
 *      );
 * -----------------------------------------------------------------------------
 * @version 1.0
 * @author  Jeffrey M. Hunter  (jhunter@idevelopment.info)
 * @author  http://www.idevelopment.info
 * -----------------------------------------------------------------------------
 */
 
public class CLOBFileExample  {

    private String          inputTextFileName   = null;
    private File            inputTextFile       = null;

    private String          outputTextFileName1  = null;
    private File            outputTextFile1      = null;

    private String          outputTextFileName2  = null;
    private File            outputTextFile2      = null;
   
    private String          dbUser              = "SCOTT";
    private String          dbPassword          = "TIGER";
    private Connection      conn                = null;
   

    /**
     * Default constructor used to create this object. Responsible for setting
     * this object's creation date, as well as incrementing the number instances
     * of this object.
     * @param args Array of string arguments passed in from the command-line.
     * @throws java.io.IOException
     */
    public CLOBFileExample(String[] args) throws IOException {
       
        inputTextFileName  = args[0];
        inputTextFile = new File(inputTextFileName);
       
        if (!inputTextFile.exists()) {
            throw new IOException("File not found. " + inputTextFileName);
        }

        outputTextFileName1 = inputTextFileName + ".getChars.out";
        outputTextFileName2 = inputTextFileName + ".Streams.out";
       
    }


    /**
     * Obtain a connection to the Oracle database.
     * @throws java.sql.SQLException
     */
    public void openOracleConnection()
            throws    SQLException
                    , IllegalAccessException
                    , InstantiationException
                    , ClassNotFoundException {

        String driver_class  = "oracle.jdbc.driver.OracleDriver";
        String connectionURL = null;

        try {
            Class.forName (driver_class).newInstance();
            connectionURL = "jdbc:oracle:thin:@melody:1521:JEFFDB";
            conn = DriverManager.getConnection(connectionURL, dbUser, dbPassword);
            conn.setAutoCommit(false);
            System.out.println("Connected.\n");
        } catch (IllegalAccessException e) {
            System.out.println("Illegal Access Exception: (Open Connection).");
            e.printStackTrace();
            throw e;
        } catch (InstantiationException e) {
            System.out.println("Instantiation Exception: (Open Connection).");
            e.printStackTrace();
            throw e;
        } catch (ClassNotFoundException e) {
            System.out.println("Class Not Found Exception: (Open Connection).");
            e.printStackTrace();
            throw e;
        } catch (SQLException e) {
            System.out.println("Caught SQL Exception: (Open Connection).");
            e.printStackTrace();
            throw e;
        }
           
    }
   
   
    /**
     * Close Oracle database connection.
     * @throws java.sql.SQLException
     */
    public void closeOracleConnection() throws SQLException {
       
        try {
            conn.close();
            System.out.println("Disconnected.\n");
        } catch (SQLException e) {
            System.out.println("Caught SQL Exception: (Closing Connection).");
            e.printStackTrace();
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException e2) {
                    System.out.println("Caught SQL (Rollback Failed) Exception.");
                    e2.printStackTrace();
                }
            }
            throw e;
        }

    }
   
   
    /**
     * Method used to print program usage to the console.
     */
    static public void usage() {
        System.out.println("\nUsage: java CLOBFileExample \"Text File Name\"\n");
    }


    /**
     * Validate command-line arguments to this program.
     * @param args Array of string arguments passed in from the command-line.
     * @return Boolean - value of true if correct arguments, false otherwise.
     */
    static public boolean checkArguments(String[] args) {
       
        if (args.length == 1) {
            return true;
        } else {
            return false;
        }

    }


    /**
     * Override the Object toString method. Used to print a version of this
     * object to the console.
     * @return String - String to be returned by this object.
     */
    public String toString() {
   
        String retValue;

        retValue  = "Input File         : " + inputTextFileName    + "\n" +
                    "Output File (1)    : " + outputTextFileName1  + "\n" +
                    "Output File (2)    : " + outputTextFileName2  + "\n" +
                    "Database User      : " + dbUser;
        return retValue;
   
    }


    /**
     * Method used to write text data contained in a file to an Oracle CLOB
     * column. The method used to write the data to the CLOB uses the putChars()
     * method. This is one of two types of methods used to write text data to
     * a CLOB column. The other method uses Streams.
     *
     * @throws java.io.IOException
     * @throws java.sql.SQLException
     */
    public void writeCLOBPut()
            throws IOException, SQLException {
       
        FileInputStream     inputFileInputStream    = null;
        InputStreamReader   inputInputStreamReader  = null;
        BufferedReader      inputBufferedReader     = null;
        String              sqlText                 = null;
        Statement           stmt                    = null;
        ResultSet           rset                    = null;
        CLOB                xmlDo*****ent             = null;
        int                 chunkSize;
        char[]              textBuffer;
        long                position;
        int                 charsRead               = 0;
        int                 charsWritten            = 0;
        int                 totCharsRead            = 0;
        int                 totCharsWritten         = 0;
       
        try {

            stmt = conn.createStatement();
           
            inputTextFile = new File(inputTextFileName);
            inputFileInputStream = new FileInputStream(inputTextFile);
            inputInputStreamReader = new InputStreamReader(inputFileInputStream);
            inputBufferedReader = new BufferedReader(inputInputStreamReader);
       
            sqlText =
                "INSERT INTO test_clob (id, do*****ent_name, xml_do*****ent, timestamp) " +
                "   VALUES(1, '" + inputTextFile.getName() + "', EMPTY_CLOB(), SYSDATE)";
            stmt.executeUpdate(sqlText);
           
            sqlText =
                "SELECT xml_do*****ent " +
                "FROM   test_clob " +
                "WHERE  id = 1 " +
                "FOR UPDATE";
            rset = stmt.executeQuery(sqlText);
            rset.next();
            xmlDo*****ent = ((OracleResultSet) rset).getCLOB("xml_do*****ent");
           
            chunkSize = xmlDo*****ent.getChunkSize();
            textBuffer = new char[chunkSize];
           
            position = 1;
            while ((charsRead = inputBufferedReader.read(textBuffer)) != -1) {
                charsWritten = xmlDo*****ent.putChars(position, textBuffer, charsRead);
                position        += charsRead;
                totCharsRead    += charsRead;
                totCharsWritten += charsWritten;
            }
           
            inputBufferedReader.close();
            inputInputStreamReader.close();
            inputFileInputStream.close();

            conn.commit();
            rset.close();
            stmt.close();
           
            System.out.println(
                "==========================================================\n" +
                "  PUT METHOD\n" +
                "==========================================================\n" +
                "Wrote file " + inputTextFile.getName() + " to CLOB column.\n" +
                totCharsRead + " characters read.\n" +
                totCharsWritten + " characters written.\n"
            );

        } catch (IOException e) {
            System.out.println("Caught I/O Exception: (Write CLOB value - Put Method).");
            e.printStackTrace();
            throw e;
        } catch (SQLException e) {
            System.out.println("Caught SQL Exception: (Write CLOB value - Put Method).");
            System.out.println("SQL:\n" + sqlText);
            e.printStackTrace();
            throw e;
        }

    }

   
    /**
     * Method used to write the contents (data) from an Oracle CLOB column to
     * an O/S file. This method uses one of two ways to get data from the CLOB
     * column - namely the getChars() method. The other way to read data from an
     * Oracle CLOB column is to use Streams.
     *
     * @throws java.io.IOException
     * @throws java.sql.SQLException
     */
    public void readCLOBToFileGet()
            throws IOException, SQLException {

        FileOutputStream    outputFileOutputStream      = null;
        OutputStreamWriter  outputOutputStreamWriter    = null;
        BufferedWriter      outputBufferedWriter        = null;
        String              sqlText                     = null;
        Statement           stmt                        = null;
        ResultSet           rset                        = null;
        CLOB                xmlDo*****ent                 = null;
        long                clobLength;
        long                position;
        int                 chunkSize;
        char[]              textBuffer;
        int                 charsRead                   = 0;
        int                 charsWritten                = 0;
        int                 totCharsRead                = 0;
        int                 totCharsWritten             = 0;

        try {

            stmt = conn.createStatement();

            outputTextFile1 = new File(outputTextFileName1);
            outputFileOutputStream = new FileOutputStream(outputTextFile1);
            outputOutputStreamWriter = new OutputStreamWriter(outputFileOutputStream);
            outputBufferedWriter = new BufferedWriter(outputOutputStreamWriter);

            sqlText =
                "SELECT xml_do*****ent " +
                "FROM   test_clob " +
                "WHERE  id = 1 " +
                "FOR UPDATE";
            rset = stmt.executeQuery(sqlText);
            rset.next();
            xmlDo*****ent = ((OracleResultSet) rset).getCLOB("xml_do*****ent");
           
            clobLength = xmlDo*****ent.length();
            chunkSize = xmlDo*****ent.getChunkSize();
            textBuffer = new char[chunkSize];
           
            for (position = 1; position <= clobLength; position += chunkSize) {
               
                // Loop through while reading a chunk of data from the CLOB
                // column using the getChars() method. This data will be stored
                // in a temporary buffer that will be written to disk.
                charsRead = xmlDo*****ent.getChars(position, chunkSize, textBuffer);

                // Now write the buffer to disk.
                outputBufferedWriter.write(textBuffer, 0, charsRead);
               
                totCharsRead += charsRead;
                totCharsWritten += charsRead;

            }

            outputBufferedWriter.close();
            outputOutputStreamWriter.close();
            outputFileOutputStream.close();
           
            conn.commit();
            rset.close();
            stmt.close();
           
            System.out.println(
                "==========================================================\n" +
                "  GET METHOD\n" +
                "==========================================================\n" +
                "Wrote CLOB column data to file " + outputTextFile1.getName() + ".\n" +
                totCharsRead + " characters read.\n" +
                totCharsWritten + " characters written.\n"
            );

        } catch (IOException e) {
            System.out.println("Caught I/O Exception: (Write CLOB value to file - Get Method).");
            e.printStackTrace();
            throw e;
        } catch (SQLException e) {
            System.out.println("Caught SQL Exception: (Write CLOB value to file - Get Method).");
            System.out.println("SQL:\n" + sqlText);
            e.printStackTrace();
            throw e;
        }

    }
   
   
    /**
     * Method used to write text data contained in a file to an Oracle CLOB
     * column. The method used to write the data to the CLOB uses Streams.
     * This is one of two types of methods used to write text data to
     * a CLOB column. The other method uses the putChars() method.
     *
     * @throws java.io.IOException
     * @throws java.sql.SQLException
     */
    public void writeCLOBStream()
            throws IOException, SQLException {

        FileInputStream     inputFileInputStream    = null;
        OutputStream        clobOutputStream        = null;
        String              sqlText                 = null;
        Statement           stmt                    = null;
        ResultSet           rset                    = null;
        CLOB                xmlDo*****ent             = null;
        int                 bufferSize;
        byte[]              byteBuffer;
        int                 bytesRead               = 0;
        int                 bytesWritten            = 0;
        int                 totBytesRead            = 0;
        int                 totBytesWritten         = 0;

        try {

            stmt = conn.createStatement();

            inputTextFile = new File(inputTextFileName);
            inputFileInputStream = new FileInputStream(inputTextFile);
           
            sqlText =
                "INSERT INTO test_clob (id, do*****ent_name, xml_do*****ent, timestamp) " +
                "   VALUES(2, '" + inputTextFile.getName() + "', EMPTY_CLOB(), SYSDATE)";
            stmt.executeUpdate(sqlText);
           
            sqlText =
                "SELECT xml_do*****ent " +
                "FROM   test_clob " +
                "WHERE  id = 2 " +
                "FOR UPDATE";
            rset = stmt.executeQuery(sqlText);
            rset.next();
            xmlDo*****ent = ((OracleResultSet) rset).getCLOB("xml_do*****ent");
           
            bufferSize = xmlDo*****ent.getBufferSize();
           
            // Notice that we are using an array of bytes as opposed to an array
            // of characters. This is required since we will be streaming the
            // content (to either a CLOB or BLOB) as a stream of bytes using
            // using an OutputStream Object. This requires that a byte array to
            // be used to temporarily store the contents that will be sent to
            // the LOB. Note that they use of the byte array can be used even
            // when reading contents from an ASCII text file that will be sent
            // to a CLOB.
            byteBuffer = new byte[bufferSize];
           
            clobOutputStream = xmlDo*****ent.getAsciiOutputStream();
           
            while ((bytesRead = inputFileInputStream.read(byteBuffer)) != -1) {
           
                // After reading a buffer from the text file, write the contents
                // of the buffer to the output stream using the write()
                // method.
                clobOutputStream.write(byteBuffer, 0, bytesRead);
               
                totBytesRead += bytesRead;
                totBytesWritten += bytesRead;

            }

            // Keep in mind that we still have the stream open. Once the stream
            // gets open, you cannot perform any other database operations
            // until that stream has been closed. This even includes a COMMIT
            // statement. It is possible to loose data from the stream if this
            // rule is not followed. If you were to attempt to put the COMMIT in
            // place before closing the stream, Oracle will raise an
            // "ORA-22990: LOB locators cannot span transactions" error.

            inputFileInputStream.close();
            clobOutputStream.close();
           
            conn.commit();
            rset.close();
            stmt.close();

            System.out.println(
                "==========================================================\n" +
                "  OUTPUT STREAMS METHOD\n" +
                "==========================================================\n" +
                "Wrote file " + inputTextFile.getName() + " to CLOB column.\n" +
                totBytesRead + " bytes read.\n" +
                totBytesWritten + " bytes written.\n"
            );

        } catch (IOException e) {
            System.out.println("Caught I/O Exception: (Write CLOB value - Stream Method).");
            e.printStackTrace();
            throw e;
        } catch (SQLException e) {
            System.out.println("Caught SQL Exception: (Write CLOB value - Stream Method).");
            System.out.println("SQL:\n" + sqlText);
            e.printStackTrace();
            throw e;
        }

    }
   
   
    /**
     * Method used to write the contents (data) from an Oracle CLOB column to
     * an O/S file. This method uses one of two ways to get data from the CLOB
     * column - namely using Streams. The other way to read data from an
     * Oracle CLOB column is to use getChars() method.
     *
     * @throws java.io.IOException
     * @throws java.sql.SQLException
     */
    public void readCLOBToFileStream()
            throws IOException, SQLException {

        FileOutputStream    outputFileOutputStream      = null;
        InputStream         clobInputStream             = null;
        String              sqlText                     = null;
        Statement           stmt                        = null;
        ResultSet           rset                        = null;
        CLOB                xmlDo*****ent                 = null;
        int                 chunkSize;
        byte[]              textBuffer;
        int                 bytesRead                   = 0;
        int                 bytesWritten                = 0;
        int                 totBytesRead                = 0;
        int                 totBytesWritten             = 0;

        try {

            stmt = conn.createStatement();

            outputTextFile2 = new File(outputTextFileName2);
            outputFileOutputStream = new FileOutputStream(outputTextFile2);

            sqlText =
                "SELECT xml_do*****ent " +
                "FROM   test_clob " +
                "WHERE  id = 2 " +
                "FOR UPDATE";
            rset = stmt.executeQuery(sqlText);
            rset.next();
            xmlDo*****ent = ((OracleResultSet) rset).getCLOB("xml_do*****ent");

            // Will use a Java InputStream object to read data from a CLOB (can
            // also be used for a BLOB) object. In this example, we will use an
            // InputStream to read ASCII characters from a CLOB.
            clobInputStream = xmlDo*****ent.getAsciiStream();
           
            chunkSize = xmlDo*****ent.getChunkSize();
            textBuffer = new byte[chunkSize];
           
            while ((bytesRead = clobInputStream.read(textBuffer)) != -1) {
               
                // Loop through while reading a chunk of data from the CLOB
                // column using an InputStream. This data will be stored
                // in a temporary buffer that will be written to disk.
                outputFileOutputStream.write(textBuffer, 0, bytesRead);
               
                totBytesRead += bytesRead;
                totBytesWritten += bytesRead;

            }

            outputFileOutputStream.close();
            clobInputStream.close();
           
            conn.commit();
            rset.close();
            stmt.close();
           
            System.out.println(
                "==========================================================\n" +
                "  INPUT STREAMS METHOD\n" +
                "==========================================================\n" +
                "Wrote CLOB column data to file " + outputTextFile2.getName() + ".\n" +
                totBytesRead + " characters read.\n" +
                totBytesWritten + " characters written.\n"
            );

        } catch (IOException e) {
            System.out.println("Caught I/O Exception: (Write CLOB value to file - Streams Method).");
            e.printStackTrace();
            throw e;
        } catch (SQLException e) {
            System.out.println("Caught SQL Exception: (Write CLOB value to file - Streams Method).");
            System.out.println("SQL:\n" + sqlText);
            e.printStackTrace();
            throw e;
        }
       
    }
   
   
    /**
     * Sole entry point to the class and application.
     * @param args Array of string arguments passed in from the command-line.
     */
    public static void main(String[] args) {
   
        CLOBFileExample cLOBFileExample = null;
       
        if (checkArguments(args)) {

            try {
               
                cLOBFileExample = new CLOBFileExample(args);
               
                System.out.println("\n" + cLOBFileExample + "\n");
               
                cLOBFileExample.openOracleConnection();
               
                cLOBFileExample.writeCLOBPut();
                cLOBFileExample.readCLOBToFileGet();
               
                cLOBFileExample.writeCLOBStream();
                cLOBFileExample.readCLOBToFileStream();
               
                cLOBFileExample.closeOracleConnection();

            } catch (IllegalAccessException e) {
                System.out.println("Caught Illegal Accecss Exception. Exiting.");
                e.printStackTrace();
                System.exit(1);
            } catch (InstantiationException e) {
                System.out.println("Instantiation Exception. Exiting.");
                e.printStackTrace();
                System.exit(1);
            } catch (ClassNotFoundException e) {
                System.out.println("Class Not Found Exception. Exiting.");
                e.printStackTrace();
                System.exit(1);
            } catch (SQLException e) {
                System.out.println("Caught SQL Exception. Exiting.");
                e.printStackTrace();
                System.exit(1);
            } catch (IOException e) {
                System.out.println("Caught I/O Exception. Exiting.");
                e.printStackTrace();
                System.exit(1);
            }

        } else {
            System.out.println("\nERROR: Invalid arguments.");
            usage();
            System.exit(1);
        }
       
        System.exit(0);
    }

반응형
반응형
제품 : JDBC

작성날짜 :

FILE을 CLOB에 INSERT하고, 반대로 컬럼을 읽어 FILE로 WRITE하는 JDBC PROGRAM 예제 (JDBC 8.1 이상)



PURPOSE

text file을 읽어서 CLOB column 에 저장하는 방법과, CLOB column의 데이타를
읽어서 file로 write하는 방법을 예제를 통해서 살펴본다.


Explanation



oracle.sql package에서 제공하는 CLOB class를 이용한다.
file의 내용을 읽어 CLOB type에 저장할 때는 CLOB.getCharacterOutputStream을
이용하고, 반대로 file에 write를 위해 CLOB type을 읽을 때는
clob.getCharacterStream() 를 이용한다.

Oracle JDBC driver 8.1.x 이상 version의 classes12.zip file이 CLASSPATH에
지정되어 있어야 한다. JDK는 1.2 이상을 사용한다.

[참고] 화일을 이용하지 않고 직접 text를 CLOB에 입력하고, 입력된 CLOB
데이타를 화면에 display하기 위해서는 아래 bulletin을 참고한다.
<Bulletin No: 19360>: Subject: ORACLE.SQL.CLOB CLASS를 이용하여
4000 BYTES이상의 CLOB TEXT 데이타를 저장하고, 조회하는 예제

Example


1. 테이블의 생성과 데이타 입력

미리 다음 작업이 수행되어 있어야 하며, 이 작업도 java application내에
statement.execute를 통해 포함시킬 수 있다.

sqlplus scott/tiger
SQL> create table test_clob(id number, c clob);
SQL> insert into test_clob values (1, empty_clob());

2. ClobFile.java

다음 내용을 ClobFile.java라는 이름으로 생성한 후,

os>javac ClobFile.java
os>java ClobFile
후 out.txt file내용을 확인하고, scott.TEST_CLOB table의 데이타도 조회하여
수해이 잘 되었는지 확인한다.

import java.sql.*;
import java.io.*;
import java.util.*;
import oracle.jdbc.driver.*;

import oracle.sql.*;

public class ClobFile {

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

try {
Connection conn;

DriverManager.registerDriver( new oracle.jdbc.driver.OracleDriver() );
conn = DriverManager.getConnection( "jdbc:oracle:thin:@krint-5:1521:ORA920"

, "scott","tiger" );

conn.setAutoCommit (false);

CLOB clob = null;

Statement stmt = conn.createStatement ();

String cmd = "select * from test_clob for update";
ResultSet rset = stmt.executeQuery(cmd);
while (rset.next())
clob = ((OracleResultSet)rset).getCLOB(2);

// 아랫부분에 file로 부터 데이타를 읽어 CLOB column에 저장하는
// readFromFile()이 작성되어 있다.

readFromFile(clob);
stmt.execute("commit");

rset = stmt.executeQuery ("select * from test_clob where id=1");

if (rset.next ())
{
clob = ((OracleResultSet)rset).getCLOB(2);
String st = rset.getString(2);

if (clob != null)
System.out.println ("clob length = "+clob.length ());
}

// 아랫부분의 CLOB 컬럼의 데이타를 읽어 다시 다른 file로 write하는
// writeToFile()을 호출한다.

writeToFile(clob);
}

catch (SQLException sqle) {
System.out.println("SQL Exception occured: " + sqle.getMessage());
sqle.printStackTrace();
}

catch(FileNotFoundException e) {
System.out.println("File Not Found");
}

catch (IOException ioe) {
System.out.println("IO Exception" + ioe.getMessage());
}
}

//


// test.txt file을 읽어 test_clob.c column에 저장한다.

static void readFromFile (CLOB clob) throws Exception {
File file = new File("/home/ora920/eykim/test.txt");
FileReader in = new FileReader(file);
Writer out = clob.getCharacterOutputStream();

int chunk = clob.getChunkSize();
System.out.print("The chunk size is " + chunk);
char[] buffer = new char[chunk];
int length;

while ((length = in.read(buffer,0,chunk)) != -1)
out.write(buffer, 0, length);

in.close();
out.close();
}

//------------------------------------------------------------------
// test.clob.c column의 데이타를 읽어 out.txt file로 write한다.

static void writeToFile (CLOB clob) throws Exception {
int chunk = clob.getChunkSize();

int length;
char[] buffer = new char[chunk];
FileWriter outFile = null;
outFile = new FileWriter("/home/ora920/eykim/out.txt");
Reader instream = clob.getCharacterStream();

while ((length = instream.read(buffer)) != -1) {
outFile.write(buffer, 0, length);
}

instream.close();
outFile.close();
}

}


Reference Documents


SCR #998


반응형
반응형

출처 : http://logonjava.blogspot.com/2010/04/heap-dump-perm-area-memory-leak.html


Software 특히 Java 언어를 사용하는 Software 개발 조직에 몸담고 있지만, 마흔을 훌쩍 넘긴 나이에 이런 글을 쓰는 것이 적합한지 의심되는데 특히 국내 SW 환경을 고려한다면 몹시 우스꽝스럽다.

이젠 개발팀장도 아니고 개발실장도 아니고 그위의 관리자이지만, 아직 완전히 제품 코드로부터 역할을 분리하지 못했고, 이러한 시간이 많이 걸리고 책임 소재가 불분명한 문제를 해결할 전문 인력을 두고 있지 않기 때문에 결국 직접 하는 경우가 생긴다. 이것은 미흡한 관리 능력의 결과라고 봐도 좋겠다.

개인적으로는 이러한 일이 전혀 나쁘지 않다. 즐거운 Software Life의 하나일 뿐이다.
관리자가 이러한 삽질을 직접 하는 것이 관리 체계를 무너뜨리는 것 아니냐고 묻겠지만...

oh, give me a break.. 나중에 교육교재 만드는 데 도움이 될까해서 하는 관리 행위의 하나라고 봐주기 바람~~ ㅠ_ㅠ;;

perm gen 과 class leak
Permanent Generation 은 young과 old를 구분하는 Generational Collector 방식인 Sun (now, Oracle)의 HotSpot JVM에서 Old generation 중 한 영역이다.
lifetime이 길다고 판단된 object들을 old generation으로 옮겨서 빈번한 gc의 대상이 되지 않도록 하는 것이 generational collector의 기본 아이디어인데 permanent generation은 old 중에서도 거의 gc 대상이 될 일이 없다고 생각되는 object들을 딴 영역에서 관리하겠다는 아이디어의 산물이다.

HotSpot JVM의 Perm Area 에는 주로 자바의 클래스 객체들이나 문자열 상수 풀에 속한 String 객체들이 위치한다.
메모리 leak의 대상이 되는 것은 string constants 보다는 주로 class 객체들이다.

(class 객체는 주로 객체의 타입을 나타내는 클래스나 인터페이스를 표현하는 객체로 타입명 뒤에 .class 라는 literal을 붙임으로써 지칭할 수 있다. 예를 들어 자바 코드에서 String.class 라는 객체는 java.lang.String 이라는 클래스의 타입을 지칭하는 객체이다.)

memory leak이란 보통 reclaim되어야 할 memory space가 reclaim되지 못하고 있는 상황을 뜻하는데 Java와 같이 garbage collector 기능을 내장한 VM에서는 주로 코드 오류로 인해 gc 대상이 되는 객체를 불필요하게 reference(자바에서는 weak reference나 soft reference와 구분하여 strong reference)하는 gc 대상이 아닌 객체들이 존재하기 때문에 발생한다.

perm area 에 위치하는 class 객체들은 classloader에 의해 load 되기 때문에 이 class 객체들이 unload되는 유일한 방법은 classloader가 unload 즉, gc 되는 것이다.

보통 Java VM이 로드될 때 사용되는 classloader들(runtime에 필요한 클래스를 주로 로드하는 system classloader와 classpath에 설정된 클래스들을 로드하는 application classloader 등)은 JVM이 종료할 때까지 gc되지 않기 때문에 여기에서는 관심의 대상이 아니고, 주로 application이 dynamic하게 코드 상에서 만드는 classloader들이 관심의 대상이 된다.

즉, 다시 말해서 "gc되어야 하는 classloader 객체가 어떤 다른 strong object 에 의해 참조되는 것을 찾는 것이 오늘의 주제"이다.

OutOfMemoryError와 heap dump
heap dump 분석은 Java에서 OutOfMemoryError(이하 OOM)가 발생할 경우 원인을 찾기 위해 많이 사용된다.
OOM 시 heap dump를 자동으로 생성하는 HotSpot JVM 옵션이 있는데 이에 관해서는 앞의 다른 blog를 참고하기 바란다.

OOM은 주로 세 가지 경우에 발생한다. 하나는 전체 heap memory 부족, 또하나는 permanent area 부족, 나머지 하나는 native thread를 더 이상 생성할 수 없을 경우이다.

전체 heap memory 부족은 너무 많은 객체를 만들거나 strong reference에 의해 객체 일부가 reclaim 되지 않아서 발생하게 된다. perm area 부족은 앞과 동일한 이유이겠지만 해당 객체가 class 객체인 경우이고 전체 heap 영역 중 perm 영역이 분리되어 있기 때문에 다르게 발생하는 것이다. thread를 생성 못하는 이유는 OS 환경과 상관이 있을 테고, 또 코드 수준에서 thread를 지나치게 많이 만들거나 또 thread 종료가 제대로 안되어 leak이 발생한 때문일 것이다.

세 가지 중 어느 원인으로 OOM이 발생했는지에 대해선 OOM 에러의 메시지를 보면 쉽게 알수 있다.

heap dump 분석에 필요한 소프트웨어
heap dump 분석을 위해 사용하는 소프트웨어는 다음과 같다.
먼저 heap dump 분석 프로그램. HP에서 제공하는 jmeter 가 있고, IBM에서 제공하는 HeapAnalyzer, SAP에서 만들었다가 지금은 eclipse에 donate된 MemoryAnalyzer 등이 있다.

다들 한번씩 try는 해봤지만 지금은 swing 기반으로 된 순수 자바 프로그램인 IBM HeapAnalyzer를 쓴다.
jmeter는 예쁘게 생겼지만 지원하는 heap dump 포맷이 제약이 있었고, SAP MemoryAnalyzer는 적은 메모리로 큰 dump 파일을 읽어들일 수 있긴 하지만, 제공해주는 정보들을 이해하기 좀 어려웠다.
그러다보니 IBM HeapAnalyzer에 익숙해져버렸다.

또, 하나 많이 사용하는 것은 X 서버이다. PC 환경에서 heap analyzer를 돌리기엔 heap dump 파일이 너무 커서 메모리를 많이 사용하기 때문에 대용량 heap을 사용할 수 있는 64비트 JVM 환경(32비트 JVM에서는 max heap size를 1.4기가 정도밖에 줄 수 없다. 정확한 최대값은 모르겠음)에서 heap analyzer를 실행해야 했는데 그러다보니 PC용 X 서버가 필요했다.

요즘 사용하는 PC용 X 서버는 free open source인 cygwin-x 이다. 큰 문제 없이 쓸 수 있다.



HeapAnalyzer 실행
요즘 개발자들은 X 환경을 이해 못하는 경우가 많아서 간단하게 PC용 X 서버에서 원격지의 heap analyzer를 어떻게 실행하는지 절차를 적어본다. (예전에 motif application 개발을 잠깐 했었던 추억이 있음)

유닉스에서 주로 사용되는 윈도우 시스템인 X-Window 시스템은 서버/클라이언트 구조로 되어 있으며 X 서버는 display를 제공하는 쪽을 뜻하고, 실제 애플리케이션 프로세스가 실행되는 쪽이 X 클라이언트가 된다.

1. X 서버인 cygwin-x를 PC에서 실행하고 xterm 프로그램을 하나 실행시킨다.

2. xterm의 shell prompt 에서 "xhost +" 명령을 실행한다. xhost는 X 서버에 접속할 수 있는 X 클라이언트 호스트들에 대한 접근 제어를 하는 명령이다. cygwin-x가 PC에서 실행되고 원격지 유닉스 서버에서 heap analyzer를 띄울 것이므로 PC가 X 서버이고 유닉스 서버가 X 클라이언트 호스트이다. xhost + 다음에 호스트 명을 주지 않으면 모든 호스트에 대해서 allow한다는 뜻이다.
다음과 같이 불평할 것이다.

[mypc]/home/yoonforh 504> xhost +
access control disabled, clients can connect from any host

3. 해당 유닉스 서버로 telnet 등을 통해 접근한다.
해당 유닉스 서버의 셸에서 DISPLAY 환경 변수를 cygwin-x가 실행된 PC로 지정해야 한다.
예를 들면 ksh이나 bash에서 다음과 같이 지정한다.

export DISPLAY=<PC IP 주소>:0

보통의 X용 appliation은 실행시 -display 옵션을 통해서 지정할 수도 있다. DISPLAY 환경변수 값은 ip 주소와 display screen 번호로 구성된다.

보통 기본값인 0 혹은 0.0을 screen number로 사용한다. 윈도우 시스템 위에 실행되는 window manager 프로그램에 따라 CDE나 gnome-desktop, mwm, twm 등은 여러 개의 screen을 가지므로 screen number를 0이 아닌 다른 값으로 지정할 수도 있다. 사실 대부분의 X용 윈도우 매니저들은 multi screen을 지원한다.
사족이 길어졌다. 그냥 0번 스크린을 지정한다. ㅠ_ㅠ

4. DISPLAY가 지정된 환경에서 heap analyzer를 실행한다.
해당 셸에서 다음과 같이 heap analyzer를 실행한다. 현재 사용하고 있는 버전은 3.7이다.

jdk 5이상 가능

jdk 6이상.


java -Xmx5G -jar ha37.jar

큰 heap dump 파일을 분석할 때에는 메모리가 많이 사용되므로 heap 최대값을 5G로 줬다. dump 파일 크기에 따라 더 줘야 할 경우도 있을 것이다. 앞에서 얘기했듯이 이 JVM이 64비트인 경우에 주로 지원된다. OS나 JVM 버전, 비트 수마다 지원하는 최대 heap 크기가 다르므로 이 부분은 알아서들 체크하기 바란다.

사족이 길었지만 이 application을 remote에서 띄워보려고 했다. 매번 연구원들에게 일일이 이런 것 가이드하기 싫어서 교육용으로 부연해보았다. cygwin-x 위에 뜬 heap analyzer 아마도 상당히 촌스럽다고 느낄 것이다. 뭐, 잘생겼다고 일 잘하는 건 아니다.

5. heap dump 파일을 읽어들인다.
보통 HotSpot JVM에서 binary format으로 heap dump를 남기게 하면 heap analyzer는 "HPROF binary"라는 포맷으로 읽어들인다.
perm area 부족으로 인한 OOM인 경우는 전체 heap 부족으로 인한 OOM인 경우보다는 heap size가 작으므로 dump 파일도 상대적으로 작다. (이것도 다행이라고 생각하면 되려나 ㅎㅎ)

dump 파일이 클수록 읽는 데 시간이 많이 걸리므로 open 시켜 놓고 다른 일을 하면 되겠다. 어차피 프로세싱은 원격지 서버에서 일어나는 것이니 PC에 몹쓸 짓은 안한다.

Object Reference 관계와 ClassLoader 간 계층 관계
이제 분석할 준비가 완료되었다. 몇 가지 필요한 사전 지식에 대해 언급해본다.

ClassLoader는 계층 구조를 가진다. 이 계층 구조는 ClassLoader를 생성할 때 반드시 부모 ClassLoader를 지정하도록 되어 있는데, 기본적인 자바의 class 정의 검색 방식은 부모 ClassLoader에 정의된 class 정의를 우선으로 찾고, 그 다음에 자식 ClassLoader에 정의된 class 정의를 찾는 구조이다.

자바의 각 ClassLoader들은 내부적으로 parent 라는 멤버 필드를 가지고 있고, 이 값이 부모 클래스로더 객체가 된다.
(classloader들의 parent/chlid 관계는 class inheritance의 parent class, child class와는 아무 상관이 없다.)

Object Reference 관계는 객체 간의 참조 구조를 나타내며 directed graph 형태이다. 즉, 참조자(Referencer)를 시작점으로 피참조자(Referencee)를 끝점으로 하나의 화살표가 그려지는 단방향 그래프 형태라고 생각할 수 있다.

표준적인 용어인지는 모르겠지만, HeapAnalyzer에서는 참조자를 parent, 피참조자를 child로 표현하고 있다. 그리고, parent가 없이 strong object인 경우를 root 라고 표현하고 있다.

즉, 단방향 그래프로 이루어진 forest 형태이지만 이것을 root node가 여러 개인 tree인 것으로 표현하고 있다. 생각해보면 root node들 위에 공통된 가상의 parent node 하나가 있다고 가정하면 하나의 tree로 볼 수 있다. 이렇게 보면 parent/child 용어는 적합하다고도 볼 수 있다. (엄밀하게 보면 상호 참조나 순환 참조가 가능하기 때문에 forest나 tree라고 볼 수 없다. 하지만, 여기에서 얘기하는 parent/child 관계를 적용하는 데는 큰 무리가 없으므로 child가 parent로도 되는 경우나 descendant가 ancestor의 parent가 되는 경우가 존재한다는 것을 예외로 간주하고 rough하게 metaphor를 적용해보자.)

이제 ClassLoader 간 parent/child 관계가 Object Reference 관계에서 어떤 관계를 가지는지 생각해보자. 이게 아마 이 논의의 핵심일 것이다.

Child ClassLoader 객체는 멤버 필드로 parent를 가지고 있으며 이 parent 필드의 값이 Parent ClassLoader 객체이다.
즉, Object Reference 관계로 보면 child classloader 객체가 parent란 필드를 통하여 parent classloader를 reference하고 있다.

다시 말하면 object reference 관계에서는 child classloader 가 referencer(parent), parent classloader가 referencee(child)로 되어 parent/child 관계가 뒤바뀐다.

용어적인 유사성 때문에 혼란이 오는 것이며 서로 관련없는 관계이지만, 결과적으로는 "일반적으로 classloader 객체 간 parent/child 관계는 객체 참조간 parent/child 관계와 반대가 된다"고 생각해도 되겠다.

분석 예
이제 실 예를 들어 perm leak을 찾아보자. 사실 perm leak 이 발생하려면 ClassLoader를 동적으로 만들어 사용하는 프로그램에서 가능하기 때문에 일반적인 application에서는 이런 경우가 많지 않다. 보통은 그냥 클래스를 많이 사용하는 경우이므로 JVM 옵션으로 perm area 영역을 늘려주면 된다.

하지만, plugin 과 같은 동적 프로그램 기능을 가지고 있는 application에서는 plugin을 위한 ClassLoader를 만들어 사용하기 때문에 이러한 경우가 종종 발생한다.

먼저 heap dump 파일을 HeapAnalyzer로 연 다음 "Analysis/Search Name" 기능을 사용하여 의심이 가는 클래스로더 객체를 찾는다. 클래스 명으로 찾을 수 있는데 dump 상에는 separator가 '.'이 아니라 '/'인 점을 주의하여 찾는다.


검색 결과를 보면 해당하는 타입의 객체와 클래스 객체가 나타난다. 아래 그림에서 클래스로더 객체는 12개가 존재한다. (13개 중 한 개는 클래스로더 타입을 나타내는 class 객체)


위 그림에서 보이듯이 각 클래스로더 객체를 선택하여 오른쪽 마우스 버튼(두번째 마우스 버튼)을 클릭하면 팝업 메뉴가 보이며 이를 통해 해당 객체의 parent 객체들이나 child 객체들을 추적할 수 있다.

클래스로더 객체를 참조하고 있는 객체 즉, object reference 관계에서 parent에 있는 객체들이 클래스로더 객체의 가비지 컬렉션을 막고 있는 상황을 찾아야 하므로, 계속해서 "List parents" 기능을 사용하여 object reference tree를 거슬러 올라가보면 문제가 되는 참조자 객체를 찾을 수 있을 것이다.

경우에 따라서는 parent 객체들이 sun/reflect/DelegatingClassLoader, java/security/ProtectionDomain, java/lang/Package 등만 나타나는 경우가 있는데 이런 객체들은 일시적인 참조이며, 이 경우는 garbage collection되기를 기다리고 있는 경우이기 때문에 leak이 아니다.

각 객체별로 parent를 추적해보면 알 수 있지만, ProtectionDomain 객체와 Package 객체는 ClassLoader 와 상호참조되는 객체들이며 다른 strong reference에 의해 연결되지 않으며 DelegatingClassLoader는 HotSpot JVM 에서 reflection을 사용하여 dynamic proxy 객체를 만들 때마다 일시적으로 사용되는 클래스로더이다.

object reference 상의 parent 추적은 여러 depth에 걸쳐 일어날 것이며 해당 클래스의 소스 코드를 알고 있다면 쉽게 연결하여 이해할 수 있을 것이다.


object reference graph 추적은 perm leak이든 일반적인 memory leak이든 동일하게 사용할 수 있다. 다만 여기에서는 classloader 객체가 leak이 되는 경우에 perm area problem이 발생한다는 것을 중심으로 설명하였다.

누구에게든 이 잡다한 내용이 작은 도움이 되길 바라면서...


출처 : http://logonjava.blogspot.com/2010/04/heap-dump-perm-area-memory-leak.html
반응형
반응형

The Zip format is also used in the JAR (Java ARchive) file format, which is a way to collect a group of files into a single compressed file, just like Zip. However, like everything else in Java, JAR files are cross-platform, so you don’t need to worry about platform issues. You can also include audio and image files as well as class files.

JAR files are particularly helpful when you deal with the Internet. Before JAR files, your Web browser would have to make repeated requests of a Web server in order to download all of the files that make up an applet. In addition, each of these files was uncompressed. By combining all of the files for a particular applet into a single JAR file, only one server request is necessary and the transfer is faster because of compression. And each entry in a JAR file can be digitally signed for security (see Chapter 14 for an example of signing).

A JAR file consists of a single file containing a collection of zipped files along with a “manifest” that describes them. (You can create your own manifest file; otherwise, the jar program will do it for you.) You can find out more about JAR manifests in the JDK documentation.

The jar utility that comes with Sun’s JDK automatically compresses the files of your choice. You invoke it on the command line:

jar [options] destination [manifest] inputfile(s)


The options are simply a collection of letters (no hyphen or any other indicator is necessary). Unix/Linux users will note the similarity to the tar options. These are:

c

Creates a new or empty archive.

t

Lists the table of contents.

x

Extracts all files.

x file

Extracts the named file.

f

Says: “I’m going to give you the name of the file.” If you don’t use this, jar assumes that its input will come from standard input, or, if it is creating a file, its output will go to standard output.

m

Says that the first argument will be the name of the user-created manifest file.

v

Generates verbose output describing what jar is doing.

0

Only store the files; doesn’t compress the files (use to create a JAR file that you can put in your classpath).

M

Don’t automatically create a manifest file.

If a subdirectory is included in the files to be put into the JAR file, that subdirectory is automatically added, including all of its subdirectories, etc. Path information is also preserved.

Here are some typical ways to invoke jar:

jar cf myJarFile.jar *.class


This creates a JAR file called myJarFile.jar that contains all of the class files in the current directory, along with an automatically generated manifest file.

jar cmf myJarFile.jar myManifestFile.mf *.class


Like the previous example, but adding a user-created manifest file called myManifestFile.mf.

jar tf myJarFile.jar


Produces a table of contents of the files in myJarFile.jar.

jar tvf myJarFile.jar


Adds the “verbose” flag to give more detailed information about the files in myJarFile.jar.

jar cvf myApp.jar audio classes image


Assuming audio, classes, and image are subdirectories, this combines all of the subdirectories into the file myApp.jar. The “verbose” flag is also included to give extra feedback while the jar program is working.

If you create a JAR file using the 0 (zero) option, that file can be placed in your CLASSPATH:

CLASSPATH="lib1.jar;lib2.jar;"


Then Java can search lib1.jar and lib2.jar for class files.

The jar tool isn’t as useful as a zip utility. For example, you can’t add or update files to an existing JAR file; you can create JAR files only from scratch. Also, you can’t move files into a JAR file, erasing them as they are moved. However, a JAR file created on one platform will be transparently readable by the jar tool on any other platform (a problem that sometimes plagues zip utilities).

As you will see in Chapter 14, JAR files are also used to package JavaBeans.

출처 : http://www.linuxtopia.org/online_books/programming_books/thinking_in_java/TIJ314_034.htm

반응형

+ Recent posts