반응형

 

참고 : https://programmer.ink/think/jvm-series-jvm-tuning-jps-jstat-jmap-jhat-jstack-jinfo.html

 

jvm series: jvm tuning - jps jstat jmap jhat jstack jinfo

jvm series: jvm tuning - jps jstat jmap jhat jstack jinfo Posted by Emma on Sun, 02 Jun 2019 21:01:42 +0200 From: https://www.cnblogs.com/ityouknow/p/5714703.html Using the command of JVM can help us locate the problem easily in production monitoring and p

programmer.ink

 

1. JMAP 

 

힙 메모리 보기

$>  jmap -heap [pid]

 

덤프 생성

$> jmap -dump:live,file=[생성할 파일경로및파일명] [pid]

 

생성된 덤프를 바로 웹으로 보고 싶다면

$> jhat -J-Xmx[메모리사이즈:덤프크기보다 크게] -port [사용할포트] [생성한 파일경로및파일명]

-> ex> jhat -J-Xmx2g -port 10000 /tmp/dump.bin

 

자바 jmap으로 heap 메모리 분석(jmap -heap pid) 수행시 오류 발생 할때

 

Heap Usage:
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at sun.tools.jmap.JMap.runTool(JMap.java:201)
        at sun.tools.jmap.JMap.main(JMap.java:130)
Caused by: java.lang.RuntimeException: unknown CollectedHeap type : class sun.jvm.hotspot.gc_interface.CollectedHeap
        at sun.jvm.hotspot.tools.HeapSummary.run(HeapSummary.java:157)
        at sun.jvm.hotspot.tools.Tool.startInternal(Tool.java:260)
        at sun.jvm.hotspot.tools.Tool.start(Tool.java:223)
        at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
        at sun.jvm.hotspot.tools.HeapSummary.main(HeapSummary.java:50)
        ... 6 more

 

아래와 같이 설치가 가능함(아래는 java 1.8임)

yum --enablerepo='*-debug*' install java-1.8.0-openjdk-debuginfo.x86_64

 

위 명령 수행시 오류 날때는 버전이 맞지 않아서 임

 

아래에서 버전 확인 뒤

http://debuginfo.centos.org/7/x86_64/

 

Index of /7/x86_64

 

debuginfo.centos.org

 

wget 명령을 통해 위의 다운로드 주소 붙여넣고

 

rpm -ivh 파일명 을 통해 설치

 

2. 메모리 튜닝

 

https://nozaki.me/roller/kyle/entry/memory-usage-tuning-of-java8

 

java 다음에 옵션 추가

 

예) java -Xms256m -Xmx512m -XX:MaxDirectMemorySize=512M  -XX:MaxMetaspaceSize=512m -XX:CompressedClassSpaceSize=256M -XX:ReservedCodeCacheSize=256M

 

3. jstat (메모리 모니터링)

 

프로세스의 메모리 상태 보기

 

$> jstat [옵션] [pid] [ms] [반복회수 : 없으면 무한]

ex> jstat -gc 10222 1000

=> pid 10222에 대해 1초에 한번 프로세스 메모리 상태를 조회

 

아래 URL을 통해 GUI 값으로 확인 가능

http://nix-on.blogspot.com/2015/01/java-jstat-how-to-visualize-garbage.html

 

Java jstat - how to visualize garbage collection statistics quickly

jstat Garbage Collection visualizer jstat  is useful for getting a quick overview of the...

nix-on.blogspot.com

 

[옵션]

class : 클래스 로더의 동작에 관한 통계

compiler : 핫스팟 Just-In-Time 컴파일러 동작에 관한 통계

gc : 가비지 컬렉트된 Heap 동작에 관한 통계

gcapacity : 세대 마다 용량과 대응하는 영역에 관한 통계

gccause : gcutil의 직전 및 현재의 가비지 컬렉션 이벤트의 원인

gcnew : New 세대의 동작에 관한 통계

gcnewcapacity : New 세대의 사이즈와 대응하는 영역에 관한 통계

gcold : old 세대 및 Permanent 세대의 동작에 관한 통계

gcoldcapacity : Old 세대의 사이즈에 관한 통계

gcpermcapacity : Permanent 세대의 사이즈에 관한 통계

gcutil : 가비지 컬렉션 통계

printompilation : 핫스팟 컴파일 방법의 통계

 

 

 

4. 번외로 시스템 io 체크

 

iostat -x [인터벌] [회수]

$> iostat -x 1 10 

반응형
반응형

처음 배우는 스프링 부트 2

 

책 자체가 현재 구성과 맞지 않아 저자에게 문의하였고

(무료 IntelliJ + start.spring.io 설정 맞추기)

 

3줄 회신이 왔는데 아래를 참고하라고 하였습니다.

 

아쉬운건 변화 무쌍한 환경에서 변경되는 내역 업데이트를 위한 게시판이 없는게 많이 아쉽네요

 

회사에서 스프링 부트 관련하여 같이 공부할 책을 찾고 있는 중인데

 

결정에 많은 도움이 되었네요

 

※ 만약 oracle JDK8이 아닌 OpenJDK 8로 사용하고 plugins 방식으로 gradle 빌드하면 무조건 오류가 납니다.

 

-------------------------------------------------- 

 

**Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target**
** ... 255 more**
**Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target**
** ... 255 more**

 

-------------------------------------------------- 

 

위의 경우는 가볍게 OpenJDK 11로 다운받아 JAVA_HOME 설정후 build gradle 수행하면 잘 됩니다 !

(cacerts 방식은 Oracle JDK에만 해당되는것 같네요)

책 발매일은 모르겠지만, 이제는 JDK 11이상으로 사용하지 않을까 싶네요...

 

인텔리제이 설정 참고 : https://www.bsidesoft.com/6926

 

buildscript 방식과 plugins 방식이 존재하며, 

start.spring.io에서는 plugins(신규) 방식으로 build.gradle 가 만들어 집니다.

아래는 buildscript 방식

 

-------------------------------------------------- 

 

buildscript {
    ext{
        springBootVersion='2.2.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group 'com.bsidesoft'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

 

-------------------------------------------------- 

 

 

74 Page : 3.1 @SpringBooTest 프로젝트 파일

책 교제와 다르게 오류가 발생하여 아래 내역을 업데이트 한 파일

-> SpringBootVersion : 2.2.6

    Gradle : 4.10.1 

3.1장_SpringBootTest_74p.zip
0.08MB

 

-------------------------------------------------- 

 

 

80 Page : 3.2 @WebMvcTest 프로젝트 파일

책 교제와는 다르게 /src/main/resource/templates/book.html 추가해야 됨(book 객체를 얻어옴)

book.html 파일 내용

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>book</title>
</head>
<body>

</body>
</html>

3.2장_SpringBootTest_80p.zip
0.08MB

 

-------------------------------------------------- 

 

81 Page: 3.3 @DataJpaTest

기본적으로 lombok 를 설치하지 않으면 builder() 메서드 밑에 붉은색 줄이 가면서

문제를 야기 시킨다.

File -> Settings -> Plugins -> 상위탭에서 Marketplace 선택 -> lombok 선택 후 설치 -> ide 재기동 

 

File -> Settings -> Build, Excution, Deployment -> Compiler -> Annotation Processors 선택

우측에

Enable annotation processing 선택 -> OK

 

3.3장_SpringBootTest_81p.zip
0.08MB

 

-------------------------------------------------- 

 

 

88 Page : 3.4 @RestClientTest

만약 위의 lombok 를 체크하지 않으면 BookRestTest.java 에서 book.getTitle() 부분의 오류가 보임

3.4장_SpringBootTest_89p.zip
0.08MB

 

-------------------------------------------------- 

 

 

90 page : 3.5 @JsonTest

3.5장_SpringBootTest_91p.zip
0.08MB

 

-------------------------------------------------- 

 

 

96 Page : 신규 커뮤니티 게시판 프로젝트 Web 구성 의존성 관련(2020.05.19)

 

-------------------------------------------------- 

 

 

109 Page 예제 4-9전 까지 압축파일

build.gradle 부분의 약간 수정이 필요

 

4장_web_109p.zip
0.16MB

 

* 설정 (build.gradle)

sping boot 2.2.x 부터는 Junit 5으로 사용되기 때문에

 

 

-------------------------------------------------- 

 

	(2.2.x 부터 추가된 설정으로 Junit 5설정으로 예제 돌리면 오류)
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
    
    * 위의 부분에서 { ~ } 까지를 빼면 Junit 4설정으로 수행되기 때문에 오류 없음
    * 또한 위의 설정으로 Test들을 수행하면 @RunWith, @Before, @Test 등이 모두 오류 발생   

 

또는 아래 URL을 참고하여 JUnit 5으로 넘어가도 됨...

java.ihoney.pe.kr/525

 

[springboot] JUnit 5 적용기

이미 나온지 꽤 시간이 흐른 Junit 5를 살펴보기 시작한다.

java.ihoney.pe.kr

 

-------------------------------------------------- 

 

 

plugins {
    id 'org.springframework.boot' version '2.3.0.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

test {
    useJUnitPlatform()
}

 

-------------------------------------------------- 

 

여기 다음 부터는 비공개로 쑥 ~

 

반응형
반응형

살다보면


모든 환경에서 쓸일이 있다... -_-...




EchoServer.java 파일(1024 포트 사용 예)

 import java.io.* ;

import java.net.* ;

class EchoServer {

public static void main( String[] args )

 throws IOException {

// 포트 설정

int port = 1024 ;

// 서버 소켓 생성

ServerSocket ss = new ServerSocket(port) ;

System.out.println( "Server Ready" ) ;

// 클라이언트 연결을 계속해서 받는다.

while( true ) {

// 클라이언트 연결을 받는다.

Socket client = ss.accept() ;

// 네트워크 입출력 스트림 설정

BufferedReader net_in = 

new BufferedReader( new InputStreamReader( client.getInputStream() ) ) ;

PrintWriter net_out = 

new PrintWriter( new OutputStreamWriter( client.getOutputStream() ) ) ;

System.out.println( "Client Socket Accepted" + client ) ;

System.out.flush() ;

// 클라이언트의 데이터를 받는다.

String line ;

line = net_in.readLine() ;

// 받은 데이터를 다시 전송한다.

net_out.println( line ) ;

net_out.flush() ;

}

}

}





EchoClient.java 파일 내용


 import java.io.* ;

import java.net.* ;

class EchoClient {

public static void main( String[] args )

 throws IOException {

// 접속 대상 설정

String host = "127.0.0.1" ;

int port = 1024 ;

// 소켓을 생성

Socket s = new Socket( host , port ) ;

System.out.println( "Client Socket Created" + s ) ;

System.out.flush() ;

// 네트워크 통신을 위한 스트림 설정

Reader from_server = new InputStreamReader(s.getInputStream()) ;

PrintWriter to_server = 

new PrintWriter(new OutputStreamWriter(s.getOutputStream()) ) ;

// 콘솔 입출력을 위한 스트림 설정

BufferedReader from_user = 

new BufferedReader( new InputStreamReader( System.in ) ) ;

PrintWriter to_user = 

new PrintWriter( new OutputStreamWriter( System.out ) ) ;

// 사용자의 입력을 받는다.

String line ;

while( (line = from_user.readLine()) != null ) {

// 받은 입력은 네트워크로 전송한다.

to_server.println( line ) ;

to_server.flush() ;

// 네트워크에서 데이터를 받는다.

int char_cnt ;

char[] buffer = new char[1024] ;

char_cnt = from_server.read(buffer) ;

// 받은 데이터를 화면에 뿌린다.

to_user.write( buffer , 0 , char_cnt ) ;

to_user.flush() ;

}

}

}


특정 서버에 대한 포트 스캔 프로그램 (PortScanner.java)


 import java.net.*;

public class PortScanner {

public static void main(String args[]) {

int startPortRange=0;

int stopPortRange=0;

startPortRange = Integer.parseInt(args[0]);

stopPortRange = Integer.parseInt(args[1]);

for (int i=startPortRange; i <=stopPortRange; i++) {

try {

Socket ServerSok = new Socket("127.0.0.1",i);

System.out.println("Port in use: " + i );

ServerSok.close();

}

catch (Exception e) {

}

System.out.println("Port not in use: " + i );

}

}

}



반응형
반응형

Java 성능 모니터링에 대해 모르고 있던 5가지 사항, Part 1



JConsole과 VisualVM을 사용한 Java 성능 프로파일링

Ted Neward, Principal, Neward & Associates

요약:  잘못된 코드(또는 잘못된 코드의 작성자)를 비난하는 것은 성능 병목 현상을 찾아내고 Java™ 애플리케이션의 속도를 향상시키는 데 아무런 도움이 되지 않으며 그 어느 것도 추측할 수 없습니다. 이 기사를 통해 Ted Neward는 Java 성능 모니터링 도구에 대한 관심을 촉구하고 있으며 먼저 Java 5의 내장 프로파일러인 JConsole을 사용하여 성능 데이터를 수집 및 분석하는 방법에 대한 5가지 팁을 소개합니다.

이 기사에 태그:  성능, 애플리케이션_개발

원문 게재일:  2010 년 6 월 29 일 번역 게재일:   2010 년 11 월 02 일 
난이도:  초급 영어로:  보기 PDF:  A4 and Letter (28KB | 7 pages)Get Adobe® Reader® 
페이지뷰:  3707 회 
의견:   0 (보기 | 의견 추가 - 로그인)

평균 평가 등급 3 개 총 2표 평균 평가 등급 (2 투표수)
아티클 순위

이 시리즈의 정보

Java 프로그래밍에 대해 알고 있다고 생각하는가? 하지만 실제로는 대부분의 개발자가 작업을 수행하기에 충분할 정도만 알고 있을 뿐 Java 플랫폼에 대해서는 자세히 알고 있지 않다. 이 시리즈에서 Ted Neward는 Java 플랫폼의 핵심 기능에 대한 자세한 설명을 통해 까다로운 프로그래밍 과제를 해결하는 데 도움이 되는 알려져 있지 않은 사실을 밝힌다.

애플리케이션 성능이 나쁠 경우 많은 개발자는 공황 상태에 빠지고 만다. 그리고 이러한 성능 저하에는 합당한 이유가 있기 마련이다. Java 애플리케이션 병목 현상의 소스를 추적하는 작업은 역사적으로 매우 어려운 작업 중 하나이다. 왜냐하면 Java 가상 머신에 블랙박스 효과가 있을 뿐만 아니라 전통적으로 Java 플랫폼용 프로파일링 도구가 부족하기 때문이다.

하지만 Java 5에서 JConsole이 등장하면서 모든 것이 바뀌었다. JConsole은 명령행과 GUI 쉘에서 작동하는 내장 Java 성능 프로파일러이다. 이 프로파일러는 완벽하지는 않지만 지식이 풍부한 책임자가 성능 문제점을 지적할 때 적절하게 대응할 수 있는 방법을 제공할 뿐만 아니라 Papa Google에 문의하는 것보다 전체적으로 훨씬 더 좋은 결과를 제공한다.

5가지 사항 시리즈의 이 기사에서는 JConsole(또는 유사한 기능을 그래픽으로 제공하는 VisualVM)을 사용하여 Java 애플리케이션 성능을 모니터링하고 Java 코드의 병목 현상을 추적하는 데 사용할 수 있는 5가지 쉬운 방법을 설명한다.

1. JDK의 내장 프로파일러

많은 Java 개발자가 Java 5 이후로 프로파일러 도구가 JDK에 포함되어 있다는 사실을 모르고 있다. JConsole(또는 최근 Java 플랫폼 릴리스의 경우에는 VisualVM)은 Java 컴파일러처럼 쉽게 실행할 수 있는 내장 프로파일러이다. JDK가 PATH에 있는 명령 프롬프트에서 jconsole을 실행하거나 GUI 쉘에서 JDK 설치 디렉토리로 이동한 후 bin 폴더를 열고 jconsole을 두 번 클릭한다.

프로파일러 도구가 실행되면 실행 중인 Java의 버전과 동시에 실행 중인 다른 Java 프로그램의 수에 따라 연결할 프로세스의 URL을 묻는 대화 상자가 표시되거나 연결할 수많은 다른 로컬 Java 프로세스가 나열되며, 나열된 목록에 JConsole 프로세스 자체가 포함되기도 한다.

JConsole 또는 VisualVM

JConsole은 Java 5 이후 모든 Java 플랫폼 릴리스에 포함되어 있다. VisualVM은 NetBeans 플랫폼을 기반으로 업데이트된 프로파일러이며 Java 6 업데이트 12 이후로 처음 포함되었다. 대부분의 작업 환경이 아직까지 Java 6으로 업그레이드되지 않았기 때문에 이 기사에서는 JConsole에 중점을 두고 설명한다. 하지만 대부분의 팁은 두 프로파일러 모두와 관련된다. (참고: Java 6에 포함되고 있는 VisualVM은 독립형 다운로드이기도 하다. VisualVM을 다운로드하려면 참고자료를 참조한다.)

JConsole 작업하기

Java 5에서 Java 프로세스는 기본적으로 프로파일링되도록 설정되지 않는다. 시동 시에 명령행 인수 -Dcom.sun.management.jmxremote를 전달하면 Java 5 VM이 연결을 열게 되며 그러면 프로파일러가 연결을 찾을 수 있다. JConsole에서 프로세스가 선택되면 프로세스를 두 번 클릭하여 프로파일링을 시작할 수 있다.

프로파일러에는 고유한 오버헤드가 있으므로 이 오버헤드를 먼저 확인하는 것이 좋다. JConsole의 오버헤드를 가장 쉽게 확인하는 방법은 먼저 애플리케이션 자체를 실행한 다음 프로파일러 하에서 애플리케이션을 다시 실행하여 그 차이를 측정하는 것이다. (이 경우 애플리케이션이 너무 크거나 작아서도 안 된다. 필자는 주로 JDK에 포함된 SwingSet2 데모 애플리케이션을 사용한다.) 따라서 필자는 먼저 가비지 콜렉션 회수를 보기 위해 -verbose:gc를 사용하여 SwingSet2를 실행했다. 그런 다음 동일한 애플리케이션을 다시 실행한 후 JConsole 프로파일러를 연결했다. JConsole이 연결된 경우에는 GC 회수 스트림이 지속적으로 발생한 반면 그렇지 않은 경우에는 회수 스트림이 발생하지 않았다. 바로 이러한 차이가 프로파일러의 성능 오버헤드이다.

2. 프로세스에 원격으로 연결하기

웹 애플리케이션 프로파일러는 소켓을 통한 프로파일링용 연결을 가정하므로 원격으로 실행 중인 애플리케이션을 모니터링/프로파일링하도록 JConsole(또는 JVMTI 기반 프로파일러)을 설정하는 간단한 구성만 수행하면 된다.

예를 들어, Tomcat이 "webserver"라는 시스템에서 실행 중이고 JVM에서 JMX를 사용하고 있고 포트 9004를 청취하고 있을 경우 JConsole(또는 기타 JMX 클라이언트)에서 JMX에 연결하려면 "service:jmx:rmi:///jndi/rmi://webserver:9004/jmxrmi"라는 JMX URL이 필요하다.

기본적으로 원격 데이터 센터에서 실행 중인 애플리케이션을 프로파일링하는 데 필요한 항목은 JMX URL이다. (JMX와 JConsole을 사용하여 원격 모니터링 및 관리를 수행하는 방법에 대한 자세한 정보는 참고자료를 참조한다.)

3. 통계 추적하기

타성에 젖지 마라!

일반적으로 애플리케이션 코드의 성능 문제점을 찾는 방법은 매우 다양하지만 몇 가지 형태로 예측할 수 있다. 초기 Java 시절부터 프로그래밍해 온 개발자는 오래된 IDE를 시작한 후 코드 베이스의 주요 부분에 대한 코드 검토를 시작하여 소스에서 익숙한 "빨간색 플래그"(예: 동기화된 블록, 오브젝트 할당 등)를 찾는 경향이 있다. 프로그래밍 경력이 수 년 이내인 개발자는 아마도 JVM에서 지원하는-X 플래그를 자세히 보면서 가비지 콜렉터를 최적화하는 방법을 찾을 것이다. 그리고 초보 개발자라면 코드를 다시 작성하는 일이 없도록 하기 위해 누군가가 JVM의 마술 같은 "make it go fast" 스위치를 찾아놓았을 것을 기대하면서 Google을 검색할 것이다.

이러한 접근 방법 모두 본질적으로 잘못된 방법은 아니지만 일종의 무모한 도전이다. 성능 문제점에 대한 가장 효과적으로 대응하는 방법은 프로파일러를 사용하는 것이다. 그리고 오늘날에는 Java 플랫폼에 프로파일러가 내장되어 있으므로 사용하지 않을 이유가 없다.

JConsole에는 다음 탭을 포함하여 통계 수집에 유용한 여러 가지 탭이 있다.

  • Memory: JVM 가비지 콜렉터의 다양한 힙에 대한 활동을 추적한다.
  • Threads: 대상 JVM의 현재 스레드 활동을 조사한다.
  • Classes: 한 VM에 로드된 총 클래스 수를 검사한다.

이러한 탭(및 연관된 그래프)은 모두 Java 5 이상의 모든 VM이 JVM에 내장된 JMX 서버에 등록하는 JMX 오브젝트의 기능이다. 지정된 JVM에서 사용할 수 있는 전체 Bean 목록이 MBeans 탭에 나열되며, 해당 데이터를 보거나 이러한 조작을 실행하는 데 필요한 제한된 사용자 인터페이스와 일부 메타데이터가 함께 제공된다. (하지만 알림 등록은 JConsole 사용자 인터페이스의 범위에 해당되지 않는다.)

통계 사용하기

Tomcat 프로세스에서 OutOfMemoryError가 지속적으로 발생하고 있다고 가정하자. 어떻게 된 일인지 알아보기 위해 JConsole을 열고 Classes 탭을 클릭한 다음 클래스 수를 계속 살펴본다. 클래스 수가 지속적으로 올라가면 애플리케이션 서버 또는 코드의 어느 부분에선가ClassLoader 누수가 발생하고 있으며 머지 않아 PermGen 공간이 소진될 것이라고 간주할 수 있다. 문제점을 자세히 확인할 필요가 있는 경우에는 Memory 탭을 검사한다.

4. 오프라인 분석을 위해 힙 덤프 작성하기

프로덕션 환경에서는 많은 작업이 빠르게 수행되기 때문에 애플리케이션 프로파일러에 할애할 수 있는 시간이 많지 않을 것이다. 대신 Java 환경의 모든 사항에 대한 스냅샷을 작성하여 저장한 후 나중에 살펴볼 수 있다. JConsole에서 이 작업을 수행할 수 있으며 VisualVM에서는 더 효과적으로 수행할 수 있다.

먼저 MBeans 탭으로 이동한 후 com.sun.management 노드와 HotSpotDiagnostic 노드를 차례로 연다. 그런 다음 Operations를 선택하고 오른쪽 분할창에 있는 "dumpHeap" 단추를 누른다. 첫 번째("String") 입력 상자에 덤프할 파일 이름을 입력하여 dumpHeap에 전달하면 전체 JVM 힙에 대한 스냅샷이 작성되어 해당 파일에 기록된다.

나중에 다양한 상용 프로파일러를 사용하여 파일을 분석하거나 VisualVM을 사용하여 스냅샷을 분석할 수 있다. (VisualVM은 Java 6에서 사용할 수 있으며 독립형 다운로드로도 사용할 수 있다.)

5. JConsole이 최고는 아니다.

프로파일러 유틸리티로서 JConsole이 좋은 도구임에 틀림 없지만 그보다 더 좋은 도구도 있다. 분석 추가 기능이나 멋진 사용자 인터페이스를 갖춘 프로파일러도 있고 기본적으로 JConsole보다 더 많은 데이터를 추적하는 프로파일러도 있다.

JConsole의 진정한 매력은 전체 프로그램이 "평범한 구형 Java"로 작성되어 있다는 것이다. 이는 곧 Java 개발자라면 누구나 이와 같은 유틸리티를 작성할 수 있다는 것을 의미한다. 실제로 JDK에는 JConsole용 새 플러그인을 작성하여 JConsole을 사용자 정의하는 방법을 보여 주는 예제가 포함되어 있다(참고자료 참조). NetBeans를 기반으로 빌드된 VisualVM에서는 플러그인 개념을 훨씬 더 잘 활용한다.

JConsole(또는 VisualVM이나 기타 도구)이 원하는 기능을 제공하지 않거나, 추적하려는 항목을 추적하지 않거나, 원하는 방식으로 추적하지 않을 경우 사용자가 직접 코드를 작성할 수 있다. Java 코드가 너무 복잡해 보일지도 모르지만 언제라도 Groovy, JRuby 또는 기타 JVM 언어를 활용하여 코드를 빠르게 작성할 수 있다.

실제로 가장 필요한 것은 JMX를 통해 연결되는 사용하기 편리한 명령 도구이며, 원하는 데이터를 원하는 방식으로 정확히 추적할 수 있다.

결론

Java 성능 모니터링은 JConsole이나 VisualVM에서 끝나지 않는다. JDK에는 대부분의 개발자가 모르고 있는 수많은 도구가 있다. 이 시리즈의 다음 기사에서는 필요한 성능 데이터를 더 자세히 살펴보는 데 도움이 되는 실험 수준의 몇 가지 명령 도구에 대해 살펴본다. 이러한 도구는 일반적으로 특정 데이터를 중점적으로 다루기 때문에 완전한 프로파일러에 비해 규모가 작은 경량 도구이다. 따라서 성능 오버헤드도 발생하지 않는다.


참고자료

교육

  • "모르고 있던 5가지 사항": Java 플랫폼에 대해 모르는 것이 많았다는 것을 일깨워 주는 이 시리즈에서는 사소하게 여겼던 Java 기술을 유용한 프로그래밍 팁으로 바꿔준다. 

  • "Monitoring and management using JMX"(Sun Microsystems): JMX와 JVM의 내장 인스트루멘테이션 도구를 사용하여 Java 애플리케이션 성능을 모니터링 및 관리하는 방법에 대해 알아보자. 

  • "Mustang JConsole"(Mandy Chung 저, Java.net, 2008년 5월): JConsole Plugin API를 사용하여 사용자 정의 플러그인을 빌드하는 방법에 대해 간략하게 소개한다. 

  • "Acquiring JVM Runtime Information"(Dustin Marx 저, Dustin's Software Development Cogitations and Speculations, 2009년 6월): JConsole과 VisualVM을 포함한 JDK의 내장 모니터링 및 관리 도구에 대한 데모를 볼 수 있다. 

  • "Build your own profiler"(Andrew Wilcox 저, developerWorks, 2006년 3월): Java 5 에이전트 인터페이스와 AOP를 사용하여 사용자 정의 프로파일러인 Java Interactive Profiler를 빌드하는 방법을 보여 준다. 

  • IBM Monitoring and Diagnostic Tools for Java: Health Center는 실행 중인 IBM Java Virtual Machine을 모니터링하는 기능을 제공하는 낮은 오버헤드의 진단 도구이다. 

  • developerWorks Java 기술 영역: Java 프로그래밍과 관련된 모든 주제를 다루는 여러 편의 기사를 찾아보자. 

제품 및 기술 얻기

  • VisualVM은 여러 명령행 JDK 도구와 경량 프로파일링 기능을 통합한 비주얼 도구이다. 

토론

필자소개

Ted Neward는 글로벌 컨설팅 업체인 ThoughtWorks의 컨설턴트이자 Neward & Associates의 회장으로 Java, .NET, XML 서비스 및 기타 플랫폼에 대한 컨설팅, 조언, 교육 및 강연을 한다. 워싱턴 주의 시애틀 근교에 살고 있다.


반응형
반응형

개발자의 실수를 줄여주는 java.sql.Connection 만들기







흔히 close()를 하지 않아서 발생하는 자원 누수 현상을 줄여주는 Connection 클래스를 만들어본다.

JDBC API 사용시 흔히 하는 개발자의 실수

JDBC API를 사용하여 데이터베이스 프로그래밍을 할 때 가장 많이 사용되는 코드는 아마도 다음과 같은 형태일 것이다.

    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    
    try {
        conn = DBPool.getConnection(); //
        stmt = conn.createStatement();
        rs = stmt.executeQuery(..);
        ...
    } catch(SQLException ex) {
        // 예외 처리
    } finally {
        if (rs != null) try { rs.close(); } catch(SQLException ex) {}
        if (stmt != null) try { stmt.close(); } catch(SQLException ex) {}
        if (conn != null) try { conn.close(); } catch(SQLException ex) {}
    }

그런데 위와 같은 프로그래밍을 할 때 흔히 하는 실수가 close()를 제대로 해 주지 않는 것이다. 특히, 하나의 메소드에서 5-10개의 (PreparedStatement를 포함한)Statement와 ResultSet을 사용하는 경우에는 개발자의 실수로 같은 Statement를 두번 close() 하고 한두개의 Statement나 ResultSet은 닫지 않는 실수를 하곤 한다. 이처럼 close() 메소드를 알맞게 호출해주지 않을 경우에는 다음과 같은 문제가 발생한다.

  1. Statement를 닫지 않을 경우, 생성된 Statement의 개수가 증가하여 더 이상 Statement를 생성할 수 없게 된다.
  2. close() 하지 않으므로 불필요한 자원(네트워크 및 메모리)을 낭비하게 된다.
  3. 커넥션 풀을 사용하지 않는 상황에서 Connection을 닫지 않으면 결국엔 DBMS에 연결된 새로운 Connection을 생성할 수 없게 된다.
위의 문제중 첫번째와 두번째 문제는 시간이 지나면 가비지 콜렉터에 의해서 해결될 수도 있지만, 만약 커넥션 풀을 사용하고 있다면 그나마 가비지 콜렉션도 되지 않는다. 따라서 커넥션 풀을 사용하는 경우 Statement와 ResultSet은 반드시 닫아주어야만 한다. 하지만, 제아무리 실력이 뛰어난 개발자라 할지라도 각각 수십에서 수백줄로 구성된 수십여개의 .java 파일을 모두 완벽하게 코딩할 수는 없으며, 따라서 한두군데는 close()를 안 하기 마련이다. 운이 좋으면 빨리 찾을 수 있겠지만, 그렇지 않다면 close() 안한 부분을 찾는 데 몇십분, 몇시간, 심한 경우 1-2일 정도가 걸리기도 한다.

Statement를 자동으로 닫아주는 MVConnection 클래스 구현

실제로 필자도 앞에서 언근했던 문제들 때문에 고생하는 사람들을 종종 봐 왔었으며, 그때마다 그 버그를 고치기 위해서 소스 코드를 일일이 찾아보는 노가다를 하는 개발자들을 보기도 했다. 그래서 만든 클래스가 있는데, 그 클래스의 이름을 MVConnection이라고 붙였다. 이름이야 여러분의 입맛에 맛게 수정하면 되는 것이므로, 여기서는 원리만 간단하게 설명하도록 하겠다.

먼저, MVConnection을 구현하기 전에 우리가 알고 있어야 하는 기본 사항이 있다. JDBC API를 유심히 읽어본 사람이라면 다음과 같은 내용을 본 적이 있을 것이다.

  • Statement를 close() 하면 Statement의 현재(즉, 가장 최근에 생성한) ResultSet도 close() 된다.
  • ResultSet은 그 ResultSet을 생성한 Statement가 닫히거나, 또는 executeQuery 메소드를 실행하는 경우 close() 된다.
MVConnection은 바로 이 두가지 특성을 사용한다. 위의 두 가지 특징을 정리하면 결국 Statement만 알맞게 닫아주면 그와 관련된 ResultSet은 자동으로 닫힌다는 것을 알 수 있다. 따라서 ConnectionWrapper 클래스는 Connection이 생성한 Statement들만 잘 보관해두었다가 각 Statement를 닫아주기만 하면 되는 것이다. PreparedStatement나 CallableStatement는 Statement를 상속하고 있으므로 따로 처리할 필요 없이 Statement 타입으로 모두 처리할 수 있으므로, PreparedStatement와 CallableStatement를 위한 별도의 코드는 필요하지 않다.

다음은 MVConnection 클래스의 핵심 코드이다.

    public class MVConnection implements Connection {
        
        private Connection conn; // 실제 커넥션
        
        private java.util.List statementList; // statement를 저장
        
        public MVConnection(Connection conn) {
            this.conn = conn;
            statementList = new java.util.ArrayList();
        }
        
        public void closeAll() {
            for (int i = 0 ; i < statementList.size() ; i++) {
                Statement stmt = (Statement)statementList.get(i);
                try {
                    stmt.close();
                } catch(SQLException ex) {}
            }
        }
        
        public void close() throws SQLException {
            this.closeAll();            conn.close();
        }
        
        public Statement createStatement() throws SQLException {
            Statement stmt = conn.createStatement();
            statementList.add(stmt);
            return stmt;
        }
        
        public CallableStatement prepareCall(String sql) throws SQLException {
            CallableStatement cstmt = conn.prepareCall(sql);
            statementList.add(cstmt);
            return cstmt;
        }
        
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            PreparedStatement pstmt = conn.prepareStatement(sql);
            statementList.add(pstmt);
            return pstmt;
        }
        
        ...
    }

위 코드를 보면 Statement를 저장하기 위한 List와 그 List에 저장된 Statement 객체를 모두 닫아주는 closeAll() 이라는 메소드가 정의되어 있다. 바로 이 List와 closeAll() 메소드가 이 MVConnection 클래스의 핵심이다. Statement를 생성해주는 메소드(createStatement, prepareCall, prepareStatement)를 보면 생성된 Statement를 statemetList에 추가해주는 것을 알 수 있다. 이렇게 저장된 Statement는 실제로 Connection을 닫을 때, 즉 Connection의 close() 메소드를 호출할 때 닫힌다. (코드를 보면 close() 메소드에서 closeAll() 메소드를 먼저 호출하고 있다.) 따라서, close() 메소드만 호출하면 그와 관련된 모든 Statement와 ResultSet은 자동으로 닫히게 된다.

위 코드에서 다른 메소드들은 모두 다음과 같이 간단하게 구현된다.

    public boolean getAutoCommit() throws SQLException {
        return conn.getAutoCommit();
    }

MVConnection은 java.sql.Connection을 implements 하기 때문에, 그 자체가 Connection으로 사용될 수 있다. 따라서 MVConnection을 사용한다고 해서 특별히 코드가 많이 변경되지는 않으며 다음과 같이 전체적으로 코드가 단순하게 바뀐다.

    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    
    try {
        // MV Connection 생성
        conn = new MVConnection(DBPool.getConnection());
        stmt = conn.createStatement();
        rs = stmt.executeQuery(..);
        ...
    } catch(SQLException ex) {
        // 예외 처리
    } finally {
        // conn 만 close() 해준다.
        if (conn != null) try { conn.close(); } catch(SQLException ex) {}
    }

때에 따라서는 Connection을 close() 하지 않고 커넥션 풀에 되돌려 놔야 할 때가 있다. 그런 경우에는 다음과 같은 형태의 코드를 사용하면 된다.

    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    
    try {
        // MV Connection 생성
        conn = new MVConnection(DBPool.getConnection());
        stmt = conn.createStatement();
        rs = stmt.executeQuery(..);
        ...
    } catch(SQLException ex) {
        // 예외 처리
    } finally {
        if (conn != null) try { 
            ((MVConnection)conn).closeAll();
            DBPool.returnConnection(conn);
        } catch(SQLException ex) {}
    }

즉, Connection을 닫지 않는 경우에는 위와 같이 커넥션 풀에 반환하기 전에 closeAll() 메소드 하나만을 호출해주면 된다. 그러면 Connection과 관련된 모든 Statement, ResultSet 등이 닫히게 된다.

결론

필자의 경우는 이 글에서 작성한 MVConnection을 실제 프로젝트에 응용하여 코드 작성의 편리함 뿐만 아니라 실수로 인해서 발생하는 시스템의 버그 문제를 어느 정도 해결할 수 있었다. 특히, Statement를 생성하거나 ResultSet을 생성할 때 발생하는 커서부족 문제를 획기적으로 줄일 수 있었다. 여러분도 이 클래스를 응용하여 보다 나은 방법으로 코딩의 실수 및 자원의 낭비를 줄일 수 있는 클래스를 작성해보기 바란다.

반응형
반응형

엑셀을 처리하는 방법

1. 일반적인 방법

   - DB <---> WAS (JAVA) 파일 생성 <---> JSP 다운로드

   - 문제점 : WAS에 메모리에서 엑셀 파일을 만들기 때문에 메모리 할당 공간이 부족할 때는 OOM(Out Of Memory)으로

                 힙 덤프를 사정없이 발사 !

 

2. 대용량 엑셀 처리 방법

    - 엑셀을 생성할 때 XML 방식으로 생성함

      http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats#Excel_XML_Spreadsheet_example

    - outputStream으로 행별로 Text를 써야 메모리에 한꺼번에 올리지 않아 WAS가 OOM를 뿜으면서 죽지 않음.

       ※ 첨부된 자바파일을 참고함

    - 대용량이므로 속도에 신경 쓰지 않을 수 없다 !!!

      그러므로 Fetch Size(JAVA에서 Oracle를 사용할 때 가지고오는 row의 양...)을 조절하여 성능향샹의 효과 까지 볼수 있다.

 

후후... 재미있는건... 난 자바를 잘 모른다...

개발자들도 원인을 잘 찾지 않는다...

귀찮은 걸까... 해결하고 싶지 않을껄까...

난 인프라 담당인데 하하하 -_-

 

BigGridDemo.java

반응형
반응형


참고 사이트 (Reference Site)

  1.MAT (Eclipse Memory Analyzer) 메모리 분석 GUI 툴
    http://www.eclipse.org/mat/downloads.php 

  2. IBM Diagnostic Tool Framework for Java Version 1.5
    http://www.ibm.com/developerworks/java/jdk/tools/dtfj.html  

  3.일본 MAT 적용 참고 사이트   
     http://d.hatena.ne.jp/kakku22/20110910/1315646914 
 


heapdump는 시스템 운영 중 항상 발생할 수 있는 문제이며,

빵빵한 시스템에서는 IBM Heap Analyzer를 사용하여 분석한다

다만,

위와 같은 빵빵한(?) 시스템이 지원되지 않는 환경(Windows 32bit)에서는

분석하는 방법이 없을까 많은 고민을 하였고, 찾다보니 아에 방법이 없지만은 않았다.


1. GUI 툴 다운로드
    - 이클립스 사이트에서 dump 분석을 위한 툴을(MAT) 제공한다.
    - 위의 참고사이트 1번 항목을 방문하면 다운로드 가능하다.

 
     - 다운 받은 파일의 압축을 풀고 해당 디렉토이에 들어가면 MemoryAnlyzer.exe와 MemoryAnalyzer.ini 파일등이 보인다.
     - 32bit 환경에서 최대 할당 가능한 heap 사이즈로 로딩 가능하도록 MemoryAnlyzer.ini 파일을 수정한다.

  -vmargs
 -Xmx1372m
 
2. IBM Heapdump 분석을 위해서는 별도의 plug-in을 추가한다.
   (인터넷 검색하면 많은 내용이 나오지만 위의 DTFJ 파일 방식이 대용량의 힙 덤프 분석에는 최고다..)
   - 2번 사이트 방문하여 다운로드 한다.


위의 클릭을 하면 zip 파일을 받을 수 있다.
   
받은 zip 파일을 MAT 압축을 풀은 디렉토리 내에 DTFJ 폴더를 만들어 그안에 복사하여 놓는다.

3. MAT에 Plug-IN 설치
    - MemoryAnlyzer.exe 더블 클릭하여 MAT 를 실행함.
    - 실행화면
     

 
     -  Help -> Install New Software... 실행


   - Add.. 를 크릭하여 설치 가능한 소프트웨어를 추가함.


     - Name에는 명칭을 적고, Location 부분은 우측의 Archive를 클릭하여 다운받은 dtfj-updatesite.zip를 직접 지정함

 
    - Check 후 Next를 클릭 -> Next

 
 - 라이센스 동의 후 Finish 클릭

 

- local 설치이다 보니 보안 땜시 한번더 묻는거임... ok 살포시 눌러줌

 
 - 다시 시작함(Restart Now를 눌러)

 
  - File -> Open Heap Dump... 를 눌러 IBM Heapdump 파일을 선택하여 분석함..

 
나머지 분석과 관련된 상세한 사항은 인터넷 검색 ㄱㄱ 
반응형
반응형

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.$. : 아직 사용해 보진 않았다... 사용해야 하는데... 우선 간략하게 정리해본다... 쿠쿠... 시간나면 더 추가 ㄱ ~



반응형
반응형

출처(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);
    }

반응형

+ Recent posts