달력

5

« 2024/5 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
.. .. ..
@ 타계정에도 접근가능하게끔 시퀀스에 권한을 준다.

grant [select,sequence,alter] on 소유계정.시퀀스 to 대상계정;


- select : currval과 nextval을 사용할 수 있는 권한
- alter : sequence 변경권한을 줌
- sequence : alter와 select를 포함

 

.
:
Posted by .07274.
2013. 4. 23. 11:45

[펌] ByteBuffer 설명 정리 I.lib()/I.lib(Java)2013. 4. 23. 11:45

.. .. ..

출처 : http://my.dreamwiz.com/conbox/deepjava/nio/nio_home.htm

~~~ 깔끔한 정리

<< 복받으실 거에요~~ ^^>>

New I/O(java.nio)

1. nio 패키지 소개

New I/O는 JDK1.4에서 새로 추가된 패키지이다. JDK1.4의 정식 명칭은 Java 2 Standard Edition JDK1.4이다. 흔히 Meriln이라고 부르는데 이는 개발시 프로젝트의 이름이다. 참고로 Meriln은 중세시대 아더왕의 전설에 나오는 마법사의 이름이기도 하지만 쇠황조롱이라는 매의 일종인 새의 이름이기도 하다.

New I/O는 java.nio 패키지로 제공되는 기능으로 크게 버퍼 관리 클래스류, 확장된 네트워크 그리고 파일 I/O, 문자 집합 지원, 그리고 정규식 문자 표현에 새로운 특징들과 개선된 성능을 제공한다. java.nio 패키지는 다음과 같은 클래스류로 나누어진다.

  • java.nio
  • java.nio.channels
  • java.nio.channels.spi
  • java.nio.charset
  • java.nio.charset.spi

여기서 spi가 붙은 것을 볼 수 있는데 이는 SPI(Service Provider Interface)로 프로그래머가 제공하는 클래스로 대체할 수 있는 기능을 제공해준다. 이는 관련된 클래스들의 기본 구현을 프로그래머가 바꿀 수 있다는 뜻이 된다. 단, 이것은 특별한 경우에만 해당되므로 이런 것이 있다는 정도만 알아두자.



1> 특징.

  • 기본 데이터형용 버퍼를 클래스로 제공해 준다.
  • Character-set 인코더들과 디코더.
  • 패턴과 어울린 기능은 Perl-style 정규식들로 설정.
  • 채널, 새로운 I/O 추상화.
  • 메모리 매핑과 파일의 lock(잡금장치)를 지원해주는 인터페이스.
  • non-blocking 입출력이 가능.

2> 패키지 소개

  • java.nio.package : 자바 기본형 유형에 맞는 버퍼 클래스들.
  • java.nio.channels.package : 채널과 셀렉터.
  • java.nio.charset.package : 문자 암호화들.
  • java.nio.channels.spi.package : Service 프로바이더는 채널들을 위해 분류.
  • java.nio.charset.spi.package : Service 프로바이더는 문자셑를 위해 분류.
  • java.util.regex.package : 정규식들에 의해서 지정된 패턴들에 대해서 문자 표현에 대한 클래스들.
  • java.lang.CharSequence.인터페이스 : 다양한 종류의 문자 순서에의 통일된 read 전용 액세스를 제공.

3> 정리

java.nio 패키지에서 눈여겨 봐둘것은 지금까지 지원이 되지 않았던 io의 nonblocking의 지원이다. 이러한 nonblocking의 지원으로 주요한 장점은 크게 다음 두가지이다.

  1. 스레드는 더이상 읽기나 쓰기에 블록킹되지 않는다.
  2. selector의 도입으로 클라이언트의 많은 접속을 처리할 서버의 부하가 상당히 줄어든다.

그리고 또 다른 한가지 java.nio 패키지에서 네트워크 기능이 강화 되었다. 채널의 도입으로 새롭게 강화된 네트워크를 다루려면 반드시 java.net 패키지의 이해는 필수이다.

==> 여기서는 nio패키지를 크게 두부분으로 나누어서 강좌를 할 예정이다. 하나는 java.nio의 Buffer류 클래스와 java.nio의 Channels 클래스이다.

2. nio의 Buffer

1> Buffer 계층도

nio패키지에는 각종 데이터를 담는 Buffer류 클래스들이 있다. 이들 클래스는 모두 Buffer클래스를 상속받는데 버퍼 클래스는 여러 종류가 있어서 각각 기본형 데이터에 맞는 클래스들이 존재한다. 이들은 다음과 같은 클래스 계층도를 가진다.

가장 기본이 되는 클래스는 Buffer 클래스이며 여기에서 기본적인 메서드들이 정의된다. 이를 상속받은 기본형 데이터를 위한 Buffer클래스들이 있다. 단, boolean형에 대한 버퍼는 없다. 전반적인 클래스를 정리해 보자.

2> 클래스의 개요

클래스명
정의
Buffer 가장 상위 클래스로서 기본 메서드를 가지고 있다.
ByteBuffer byte 버퍼
ByteOrder 저장되는 바이트 순서의 열거 형태에 따른 버퍼
CharBuffer char 버퍼
FloatBuffer float 버퍼
IntBuffer int 버퍼
LongBuffer long 버퍼
MappedByteBuffer 파일의 메모리 맵 영역을 내용으로 하는 direct byte 버퍼(데이터가 저장되는 메모리에 따른 버퍼)
ShortBuffer short 버퍼
DoubleBuffer double 버퍼

표을 보면 낯설은 클래스가 두개 보인다. 바로 MappedByteBuffer 클래스와 ByteOrder 클래스이다.

먼저 MappedByteBuffer 클래스는 메모리 관리방식이 다른 클래스와 다른데, 버퍼는 만들기에 따라서 자바 가상머신내에 객체가 저장되는 메모리 공간인 Heap 버퍼가 있고 그외 다른 것들(변수들)이 저장되는 일반 버퍼가 있다. 여기서 Heap영역이 아닌 일반 메모리에 직접 접근하는 버퍼를 Direct Buffer라고 표현하는데 이 곳을 메모리 공간을 잡는 클래스이다.

ByteOder 클래스는 데이터가 어떤 순으로 구성될 것인지를 나타낸다. 컴퓨터는 두 바이트이상을 처리할때 처리효율이나 CPU 디자인 상의 문제로 데이터의 앞 바이트부터냐 뒤 바이트부터냐에 대한 해석의 순서에 차이가 있다. 이러한 차이는 데이터를 외부로 보내거나 받을때 영향을 미치기 때문에 데이터를 다루는 버퍼도 이를 고려해야 한다. 앞 바이트를 먼저 처리하는 CPU를 Big endian 이라고 표현하고 뒤 바이트를 먼저 처리하는 CPU는 앞 바이트에서 끝난다고 해서 Little endian 이라 한다. 만약 Little endian으로 동작하는 컴퓨터에서 만든 데이터 파일을 Big endian로 동작하는 컴퓨터에서 읽어 들여야 한다면 ByteOder 클래스로 데이터를 맞추어서 제대로 된 데이터를 얻을 수 있다. ByteOder 클래스의 클래스 메서드인 nativeOrder()메서드는 현재 동작하고 있는 컴퓨터의 CPU가 Big endian인지 Little endian인지 알려준다. 그럼 테스트를 해보자. 당장 메모장을 열고 다음과 같이 코딩을 해보자.........


/* ByteOrder 클래스의 nativeOrder() :
컴퓨터가 데이터를 어떤 순으로 처리하는지 검사
*/

import java.nio.*;

class BigLittleTest {
public static void main(String[] args) {

System.out.println("운영체제 종류 : "+System.getProperty("os.name")+"\n"+
"기본이 되는 네이티브의 바이트 순서 :"+ByteOrder.nativeOrder());

}
}

3> 예외 클래스의 개요

버퍼 클래스에서도 예외가 있는데 주로 버퍼가 다 찼을때, 데이터를 집어넣으러고 하는 경우, 버퍼에서 읽어올 데이터가 없을경우 발생하는 예외다. 역시 이들 예외도 클래스로 미리 정의 되어 있다. 다음 표는 버퍼에 관련되 예외 클래스이다.

클래스명
정의
BufferOverflowException
데이터를 put()할때 버퍼의 limit에 이르렀을 때 슬로우 되는, 체크되지 않는 예외.
BufferUnderflowException 데이터를 get()할때 버퍼의 limit에 이르렀을 때 슬로우 되는, 체크되지 않는 예외.
InvalidMarkException 마크가 정의되어 있지 않은 상태로 버퍼를 리셋트 하려고 했을 때에 슬로우 되는, 미검사 예외.
ReadOnlyBufferException read 전용의 버퍼상에서 put 나 compact 라고 하는 컨텐츠 변경 메소드가 호출되면, 발생하는 예외.

BufferOverflowException나 BufferUnderflowException 경우 버퍼 클래스류를 사용하면서 가장 많이 발생하게 될 예외가 될 것이다.....

자, 이제 버퍼 클래스를 자세히 살펴보자. 각 클래스의 메서드들은 파라미터 값만 각 데이터에 맞게 다르고 중복되는 메서드가 많으므로 주요 메서드를 위주로 살펴본다.

자바가상머신은 Big endian이다....

자바가상머신도 일종의 독립된 컴퓨터이기 때문에 이런 문제를 취급하는데 자바는 어떤 환경이든 실행되어야 한다는 기본 전제가 있다. 그래서 자바 클래스 파일은 실행되는 컴퓨터의 CPU에 관계없이 무조건 Big endian으로 동작하게끔 되어있다. 사실 이런 과정은 자바가상머신이 외부 CPU와 데이터 교환을 할때 자동적으로 처리되므로 사용자가 신경쓸 필요가 없다.

3. 버퍼 기본 동작

Buffer 클래스류들을 살펴 보기전에 버퍼의 일반적인 동작에 대해 알아보자.

1> 버퍼의 기본 구조

버퍼는 시작과 끝이 잇는 일직서의 모양의 데이터 구조를 가진다. 버퍼는 객체 생성시 크기가 결정이 되며 한번 결정된 크기는 절대로 변하지 않는다. 따라서 크기를 늘이고자 한다면 다시 객체를 생성해 주어야 한다. 기본형 데이터들은 데이터 유형에 맞게 버퍼 객체를 생성해서 조작을 하며 이들 클래스들의 공통된 동작을 위해 java.nio.Buffer 클래스가 존재한다.

Buffer는 데이터를 관리하게 위해서 position,limit,capacity라는 중요한 값을 가지고 있다.

  • position : 현재 읽거나 쓸 버퍼에서의 위치값이다. 인덱스 값이기 때문에 0부터 시작하며, 음수를 가지거나 limit보다 큰값을 가질 수 없다. 만약 position과 limit 의 값이 같아진다면 더이상 데이터를 쓰거나 읽을 수 없다는 뜻이 된다.
  • limit : 버퍼에서 읽거나 쓸 수 있는 위치의 한계를 나타낸다. 이 값은 전체 버퍼에서 얼마큼의 메모리 공간을 사용할 것인지를 지정하는 것으로 capacity 보다 작거나 같은 값을 가지며 음수 값은 가지지 않는다. 인덱스 값이기 때문에 0부터 시작하며 최초 버퍼를 만들었을때는 capacity와 같은 값을 가진다.
  • capacity : 버퍼가 사용할 수 있는 최대 데이터 개수(메모리 크기)를 나타낸다. 크기는 음수가 되지 않으며 한번 만들어지면 절대 변하지 않는다. 인덱스 값이 아니라 수량임을 주의하자,.

    그외...
  • mark : reset 메소드를 실행했을 때에 돌아오는 위치를 지정하는 인덱스로서 mark()로 지정할 ?있다. 주의할 것은 지정시 반드시 position 이하의 값으로 지정해 주어야 한다. position나 limit의 값이 mark 값보다 작은 경우,mark는 실행되지 않는다. mark()가 정의되어 있지 않은 상태로 reset 메소드를 호출하면 ,InvalidMarkException 가 발생한다.

각 값들의 관계는 다음과 같다.

0 <= mark <= position <= limit <= capacity

예를 들어 전체 크기가 10인 버퍼를 생성했다면 이를 다음과 같이 그림으로 나타내 본다면...,

0
1
2
3
4
5
6
7
8
9
(pos)
(limit) (capacity)

각 숫자는 버퍼의 인덱스를 나타내고 처음 버퍼가 생성되면 그 초기값은 position이 0, limit는 capacity 값과 같으며 capacity 는 버퍼의 전체 크기를 가진다.

버퍼에서는 데이터를 효과적으로 다루기 위해서 capacity 안에서 position과 limit을 적절히 조절하게 된다.따라서 이들 세 값들을 잘 다룬다면 버퍼는 사용하기는 아주 편리할 것이다.

2> Buffer류 클래스의 get/put(읽기/쓰기) 메서드

버퍼의 동작에서 가장 중요한 동작은 바로 데이터를 읽고 쓰기일 것이다. 이때 사용하는 것이 put()과 get()이다. 이 메서드는 모든 버퍼류 클래스에서 사용하는데 크게 상대적 get/put과 절대적 get/put이 있다. 우선 상대적 get/put을 설명한다.

1) 상대적 get/put(읽기/쓰기)

상대적이라고 하는 이유는 position의 위치에 상대적인 동작을 하기 때문인데 position이 limit를 향해 읽거나 쓴만큼 증가한다. 따라서 상대적인 get/put 동작을 버퍼에 하면 현재 position에 있는 내용을 읽거나 positon 위치에 데이터를 쓴 다음 position값이 증가한다. 이 값은 limit과 같을 때까지 증가한다. 일단 position값이 limit값과 같아지면 그 다음부터는 get/put 동작을 할 수 없고 이들 값을 다시 재설정하는 메서드를 사용해야 한다. 만약 position값이 limit값까지 증가했는데도 get() 메서드를 사용한다면 BufferUnderflowException이 발생하며 put() 메서드를 사용한다면 BufferOverflowException이 발생한다.

2) 절대적 get/put(읽기/쓰기)

절대적이라 함은 get/put 동작시 현재의 position에 영향을 받지 않는 상태에서 limit 한도내에서 절대위치의 값을 읽거나 쓸 수 있다는 뜻이다. get/put 메서드 중에 인덱스 값을 인자로 받는 메서드가 있는데 이들 메서드를 사용해서 절대위치의 값을 읽거나 쓸 수 있다.

한꺼번에 여러 개의 데이터를 get/put 를 할 수 있는데 메서드 인자로 해당 데이터 배열을 넣어주면 된다. 예를 들어 CharBuffer클래스인 경우, get(char[] c)로, ByteBuffer클래스인 경우 put(byte[] b)로 사용한다. 이것은 position에 대해 상대적인 동작을 하며 역시 인자로 인덱스값을 주는 경우 절대위치에서 조작을 할 수 있다.

==> get/put 동작은 get()과 put()메서드로 수행되는데 이 메서드는 Buffer 클래스의 하위 클래스류에서 모두 가지고 있다. 동작은 같으나 인자나 리턴형은 다루는 데이터형-클래스-에 따라 다르다.

3> 테스트하기

이제 버퍼를 사용하는 간단한 예제를 다루어 보자. 우선 버퍼를 사용하려면 버퍼를 생성해야 한다. 당연하다. 버퍼를 생성하는 것은 간단하다. 각 XXXBuffer 클래스이 가지고 있는 클래스 메서드인 allocate() 메서드를 사용하면 간단하게 버퍼를 생성할 수 있다. 이때 인자로 버퍼의 초기 크기를 지정해준다. 형식은 다음과 같다.

IntBuffer buf=IntBuffer.allocate(10);

이는 int형 데이터를 다루는 크기가 10인 버퍼를 생성한다. 이제 예제를 보자.

import java.nio.*;

public class BufferTest1 {
public static void main(String[] args) {
//크기가 10인 IntBuffer버퍼 객체 생성
//이때 초기값은 pos는 0, limit는 capacity 값과 같다.
IntBuffer buf=IntBuffer.allocate(10);
System.out.println(buf);

buf.put(11);
System.out.println(buf.get(0));
System.out.println(buf);

buf.put(12);
System.out.println(buf.get(1));
System.out.println(buf);

buf.put(13);
System.out.println(buf.get(2));
System.out.println(buf);

buf.get(7);
buf.put(14);
buf.get(9);

System.out.println(buf);
}
}

<< 실행 결과 >>


C\>java BufferTest1

java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
11
java.nio.HeapIntBuffer[pos=1 lim=10 cap=10]
12
java.nio.HeapIntBuffer[pos=2 lim=10 cap=10]
13
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
java.nio.HeapIntBuffer[pos=4 lim=10 cap=10]

우선 버퍼 객체를 생성하고 이를 그대로 화면에 출력을 하면 position, limit, capacity가 차례대로 출력됨을 알 수 있는데 이는 toString()메서드 때문이다. 맨처음 출력은 버퍼의 초기값을 출력해 준다. pos는 0이고 lim과 cap는 값이 같다. 그리고 put()메서드를 이용해서 버퍼의 0번자리에 '11'이라는 값을 넣고 get()을 이용해서 출력한다. 그리고 나서 버퍼 객체를 출력하면 pos가 1칸 이동한 것을 알 수 있다. 그리고 총 5번의 put()메서드를 이용해서 값을 버퍼에 넣으니 pos가 put한 만큼 이동한 것을 알 수 있다. 결과를 보면 get을 하고 나면 pos는 이동하지 않는 다는 것을 알 수 있다. 이를 그림으로 나타내면... 다음과 같다.

11
12
13
14
0
0
0
0
0
0

0
1
2
3
4
5
6
7
8
9

(pos=4)
(limit,capacity=10)


Heap Buffer와 Direct Buffer

위의 예제 소스 결과에서 IntBuffer 객체를 출력하니 java.nio.HeapIntBuffer가 출력됨을 알 수 있다. 이는 버퍼 객체를 생성하면 메모리에 적절한 내부 구현 객체가 생기는데 allocate() 메서드로 생성하면 Heap Buffer를 얻을 수 있다. 이는 앞서 MappedByteBuffer 클래스 소개시 잠깐 언급을 했는데 Heap Buffer는 자바 가상머신내에 객체가 저장되는 메모리 공간인 Heap 버퍼를 말한다. 따라서 allocate() 메서드로 생성된 버퍼에서는 데이터가 Heap 버퍼에 저장이 된다. 하지만 이 공간이 아닌 일반 메모리 공간에 버퍼를 생성할 수 있는데 이를 Direct Buffer라 한다. 이 버퍼는 allocateDirect()메서드로 생성할 수 있다. Direct Buffer로 만들면 운영체제가 제공하는 메모리 입출력기능을 직접 쓰기때문에 입출력 속도가 매우 빠른 장점을 가지고 있다. 하지만 만들때에는 시간이 걸린다. 따라서 Direct Buffer는 대용량의 데이터를 다루고 오랜 기간동안 쓰면서 빠른속도가 필요한 곳에 적합하다. 단, Direct Buffer는 ByteBuffer 클래스만 사용가능하다.

4. Buffer 클래스(java.nio.Buffer)

nio 패키지에서는 boolean형을 제외한 자바 기본 데이터형을 다룰 수 있는 Buffer류 클래스를 제공해 준다. Buffer 클래스는 버퍼로서의 기본적인 기능을 정의하며 이를 상속받는 기본형 데이터를 위한 Buffer 클래스들이 있다. 메모리 매핑 버퍼인 MappedByteBuffer 클래스와 CPU 타입에 따른 Big endian과 Little endian 구분을 위한 ByteOrder 클래스도 있다. 이들 클래스는 다루는 데이터형에 따라 XXXBuffer 라는 이름으로 클래스가 정의 되어있는데 그중에서 가장 상위 클래스는 Buffer 클래스이다. 따라서 버퍼 클래스류들은 이 Buffer 클래스가 가지고 있는 메서드는 다 사용할 수 있다.

이제 기본 메서드를 살펴보자.

1> Buffer 클래스의 메서드

ㅁ public final int capacity() : 이 버퍼의 용량(전체크기)를 리턴한다.

ㅁ public final int position() : 이 버퍼의 위치를 리턴.
ㅁ public final Buffer position(int newPosition) : 이 버퍼의 위치를 newPosition으로 설정한다.

ㅁ public final int limit() ; 이 버퍼의 limit를 리턴.
ㅁ public final Buffer limit(int newLimit) : 이 버퍼의 limit를 newlimit으로 설정한다.

ㅁ public final Buffer mark() : 이 버퍼의 현재 위치에 마크를 설정.

ㅁ public final Buffer reset()
: 버퍼의 위치를 이전에 마크 한 위치에 되돌린다. 그렇다고 마크의 값이 마크가 파기되거나, 변경되지 않는다.
==> 예외: InvalidMarkException - 마크가 설정되어 있지 않은 경우에 reset()메서드를 호출한 경우 발생

ㅁ public final Buffer clear()
: 이 버퍼의 위치(position)는 0으로 limit와 capacity값과 같게 설정한다. 마크값은 파기된다. 한 번 사용한 버퍼를 새롭게 다시 쓰기 위해 설정한다. 이 메소드는, read 조작 (put)을 실행하기 전에 주로 호출한다. 단, 버퍼내의 데이터를 지우는 것은 아니다. 단지 position과 limit를 초기값으로 설정한다.

ㅁ public final Buffer flip()
: 이 버퍼의 위치(position)는 0으로 limit와 position값과 같게 설정한다. 마크값은 파기된다. 이 메서드는 read 조작 (put)뒤, write()나 get()하기전에 이 메소드를 호출한다.

==> 예 buf.put(magic); // Prepend header
in.read(buf); // Read data into rest of buffer
buf.flip(); // Flip buffer
out.write(buf); // Write header + data to channel

ㅁ public final Buffer rewind()
: 이 버퍼의 위치만 0 으로 설정되어 마크는 파기된다. 이는 이미 put한 것을 다시 get할때나 이미 put한 것을 취소할때 사용한다.

ㅁ public final int remaining()
: 현재 위치에서 limit까지 읽어들일 수 있는 데이터의 개수를 리턴한다.

ㅁ public final boolean hasRemaining()
: 현재 위치에서 limit까지 하나 이상의 차이가 나는지를 boolean형으로 리턴해 준다. 하나 이상의 차이가 나면 하나 이상의 데이터를 get()하거나 put()할 수 있기 때문에 BufferOverflowException 이나 BufferUnderflowException 을 피하기 위한 목적으로 사용된다. 이 버퍼내에 요소가 1 개 이상 존재하는 경우 true를 리턴.

ㅁ public abstract boolean isReadOnly()
: 현재 이 버퍼가 get()만 가능하며 put()은 금지되어있는지 알려준다.

2> 예제


import java.nio.*;

class BufferMethod {
public static void main(String[] args) {
//크기가 10인 IntBuffer 클래스 객체를 생성.
IntBuffer buf=IntBuffer.allocate(10);
System.out.println("생성된 후:"+buf);

for(int i=1;i<=5;i++){
buf.put(i);//1에서 5까지 버퍼에 저장
}
System.out.println("1에서 5까지 저장후 : "+buf);

buf.flip();//pos:0, limit=pos
System.out.println("flip():"+buf);

System.out.println("remaining():"+buf.remaining());//읽어들일수 있는 데이터수

buf.clear();//pos:0 , limit=cap
System.out.println("clear():"+buf);

System.out.println("remaining():"+buf.remaining());

for(int i=0;i<10;i++){
System.out.print(buf.get(i)+",");//출력

}

buf.position(5);//현재 버퍼의 위치를 5로 지정

System.out.println("\n"+"버퍼의 pos를 5로 설정한 후 :"+buf);

for(int i=101;i<=105;i++){
buf.put(i);//1에서 5까지 다시 다른 값을 버퍼에 저장
}

buf.rewind();//pos 값만 0으로

while(buf.hasRemaining()){
System.out.print(buf.get()+",");//출력
}
System.out.println("\n"+"isReadOnly() : "+buf.isReadOnly());

}
}

<< 실행 결과 >>


C\>java BufferMethod

생성된 후 : java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
1에서 5까지 저장후 : java.nio.HeapIntBuffer[pos=5 lim=10 cap=10]
flip() : java.nio.HeapIntBuffer[pos=0 lim=5 cap=10]
remaining() : 5
clear() : java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
remaining():10
1,2,3,4,5,0,0,0,0,0,
버퍼의 pos를 5로 설정한 후 : java.nio.HeapIntBuffer[pos=5 lim=10 cap=10]
1,2,3,4,5,101,102,103,104,105,
isReadOnly() : false

이 예제는 편의를 위해 IntBuffer를 사용했다. 소스를 보면 먼저 크기 10의 버퍼를 만들고 화면에 출력을 했는데 position이 0이고 limit와 capacity가 10임을 확인할 수 있다.(pos=0 lim=10 cap=10) 이것이 버퍼의 초기값이다. 그러고 나서 put()을 한 이후에 position이 put()한만큼 이동한 것을 알 수있다.(pos=5 lim=10 cap=10) 그리고 flip() 메서드를 호출하고 확인하면 position가 0으로 가고 limit가 position위치로 오는 것을 확인한다.(pos=0 lim=5 cap=10) remaining()메서드로 position에서 limit까지 5개의 데이터가 있음을 알수 있다. clear()를 호출해서 다시 limit를 capacity와 같은 값으로 설정하고(pos=0 lim=10 cap=10) remaining()메서드를 호출하면 이번에는 position에서 limit까지 10개의 데이터가 있음을 알수 있다. get을 해서 화면에 데이터를 출력하면 값이 10개 출력됨을 알 수 있다.(1,2,3,4,5,0,0,0,0,0) position(5)으로 설정을 하고(pos=5 lim=10 cap=10) 101에서 105까지 값을 put한다. position를 5로 설정한 이유는 기존의 값을 유지하고 다음 데이터를 이어쓰기위해서이다. 다시 101에서 105까지 put하고 나면 position는 10이되는데 이를 get하기 위해 rewind()메서드를 호출해서 다시 position을 0으로 설정한다.(pos=0 lim=10 cap=10) 이렇게 채워진 값들을 while 반복문에서 hasRemaining()를 조건으로 검사해서 get한다. 좀 복잡한거 같지만 천천히 그림을 그려 보면 훨씬 이해가 쉽다. 다음 그림을 보고 정리하자...

1. 버퍼 생성 --> 아직 데이터가 put되지 않았다. 그럼 기본 초기값이 들어간다.

0
0
0
0
0
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=capacity=10)

2. 1에서 5까지 put한다.

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=5)
(limit=capacity=10)

3. flip() 메서드 호출

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=5)
(capacity=10)

4. clear() 메서드 호출

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=capacity=10)

5. position(5) 메서드 호출

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=5)
(limit=capacity=10)

6. 101에서 105까지 값을 put()

1
2
3
4
5
101
102
103
104
105
0
1
2
3
4
5
6
7
8
9
(pos=limit=capacity=10)

7. rewind()메서드 호출

1
2
3
4
5
101
102
103
104
105
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=capacity=10)

8. 마지막으로 while 반복문에서 hasRemaining()를 조건으로 검사해서 get해서 데이터를 출력...끝......

메서드의 연쇄호출 가능이 가능하다.

위 메서드 대부분이 position의 이동을 설정하는 것이다. 그럼 내가 원하는 위치를 얻기위해서 여러 메서드르 호출해야 하는 경우가 많다. 이런 경우 하나하나 호출하는 것보다 연결해서 사용할 수도 있다. 예를 들어 다음과 같은 문장이 있다고 하자.

b.flip();
b.position(23);
b.limit(42);

이것들은, 보다 편한 방식으로 한다면...다음과 같이 연결해서 코딩한다.

b.flip(). position(23). limit(42);

5. Buffer의 하위 클래스

버퍼류 클래스에는 boolean형을 제외한 나머지 기본형 데이터형에 맞는 Buffer 클래스들이 있다. 이들 클래스들은 각각의 데이터형에 맞는 여러 기능의 메서드를 가지고 있다. 또한 이들 하위 클래스들의 특징은 모두들 abstract 클래스인데 그 이유는 Heap Buffer와 Direct Buffer의 구분때문이다. 이들을 객체화 할때 allocate()메서드를 이용하는데 이때는 Heap Buffer를 사용한다. Direct Buffer를 사용하기 위해서는 ByteBuffer 클래스의 allocateDirect()메서드를 이용하는데 이는 ByteBuffer만 가능하다. 하지만 다른 유형의 버퍼로 변환하는 것은 가능하다. 따라서 버퍼류 클래스 객체 생성을 하는 allocate()메서드는 각각 클래스에 맞게 정의되어 있어서 그형의 클래스 객체를 리턴한다.

각 버퍼류 클래스마다 공통된 메서드가 거의 주류를 이루므로 한꺼번에 설명하겠다. 기능은 같으나 각 인자형이나 리턴형은 각 클래스에 따라 다르다라는 것을 알고 하나씩 살펴보자.

1> Buffer의 하위 클래스들의 주요 메서드

ㅁ allocate(int capacity)
:
각각의 버퍼 클래스형 객체 리턴(Heap Buffer)

ㅁ wrap(기본형 데이터 배열)
ㅁ wrap (기본형 데이터 배열, int offset, int length)
:
해당 기본형 데이터 유형 배열을 인자로 주면 이 배열이 해당 버퍼에 put되어서 클래스형 객체 리턴(Heap Buffer), 즉 버퍼생성과 동시에 데이터 저장이다.

ㅁ array()
: 현재 버퍼가 데이터를 저장하는데 쓰고 있는 기본형 데이터형의 배열을 리턴한다. 단 이 메서드는 hasArray() 메소드가 true를 리턴하는 경우에만 사용한다.hasArray() 메소드가 true를 리턴하려면 Direct Buffer가 아니며 데이터 관리를 배열로 하고 있는 경우여야 한다.

ㅁ abstract boolean isDirect ()
:
현재 버퍼가 Direct Buffer인지 아닌지를 리턴. Direct Buffer이면 true를 리턴한다.

ㅁ asReadOnlyBuffer()
: read 만 가능한 버퍼를 리턴.

ㅁcompact()
: 현재 position과 limit 사이에 남아있는 데이터들을 모두 버퍼 맨 앞쪽으로 이동시킨다. 그리고 position은 이들 데이터의 맨 마지막 칸으로 동하고 limit는 capacity와 같은 값을 가진다. 이메서드는 다른 메서드와는 달리 위치값뿐만아니라 데이터들의 이동이 있다는 점이 다르다.
예를 들어 다음 그림과 같이 현재 position이 4이고 limit가 7이라고 하자.

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

이 상태에서 compact()메서드를 호출하면 position과 limit 사이의 데이터들을 맨 앞으로 이동시키고 위치는 그 다음 칸으로 이동하고 limit는 capacity와 같은 값을 가진다.

5
6
7
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=3)
(limit=cap=10)

ㅁ slice()
: 현재 position과 limit 의 범위를 별도의 버퍼로 만들어서 리턴해 준다. 즉 잘라내기라 생각하면 된다. 이것은 데이터는 공유하지만 position과 limit , mark값을 별도로 갖는 것으로 어느 한쪽에서 데이터를 수정하면 다른 한쪽도 수정한 데이터를 볼 수 있다. 이렇게 만들어진 새로운 버퍼는 position는 0으로, limit는 capacity와 같은 값을 가진다. mark는 재설정해 주어야 한다. 또 버퍼가 Heap Buffer라면 새로 생긴 버퍼도 Heap Buffer이며, 읽기전용이면 새 버퍼도 읽기전용이다.
예를 들어 다음 그림과 같은 버퍼를 slice()를 호출해서 새로 버퍼를 만든다면.. 그림과 같다.

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

5
6
7
0
1
2
<--새로 생긴 버퍼
(pos=0)
(limit=cap=10)

ㅁ duplicate()
: 데이터를 공유하는 또 하나의 버퍼를 생성해 주는데 이는 데이터 값만 복사되는 slice()와는 달리 position과 limit , mark 같은 모든 값이 그대로 복사된다. 즉 통째로 복사가 된다. 다만 복사된 이후에 position과 limit , mark값을 별도로 가진다. 그래서 내용만 공유하고 position과 limit , mark값는 따로 조작이 가능하다.
예를 들어 다음 그림과 같은 버퍼를 duplicate()를 호출해서 새로 버퍼를 만든다면.. 그림과 같다.

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

데이터를 공유하는 또 다른 버퍼 생성

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

2> 예제


import java.nio.*;

class SubBuffer {
public static void main(String[] args) {

System.out.println("-----------wrap()로 배열을 통째로 버퍼에 넣기 ");
int i[]={10,20,30,40,50,60,70,80,90,100};
// wrap()로 버퍼 생성과 동시에 데이터 put
IntBuffer buf=IntBuffer.wrap(i);
// 제대로 들어갔는지 데이터를 get해서 확인
while(buf.hasRemaining()){
System.out.print(buf.get()+",");
}
// position/limit/capacity확인
System.out.println("\n넣은후 : "+buf);

System.out.println("\n-----------array()로 반대로 버퍼를 배열로------");
// 버퍼의 데이터를 배열로 리턴, 이때 hasArray()가 true를 리턴해야 된다.
if(buf.hasArray()){
int a[]=buf.array();

// 배열 출력
for(int x=0;x<a.length;x++)
System.out.print(a[x]+",");
}
System.out.println("\n넣은후 : "+buf);

System.out.println("\n-----------compact()로 3부터 10까지 테이터를 맨앞으로------ ");
buf.position(3);
buf.compact();
System.out.println(buf);
for(int s=0;s<10;s++){
System.out.print(buf.get(s)+",");//출력
}

System.out.println("\n\n-----------duplicate()로 복사하기------- ");
IntBuffer buf2=buf.duplicate();
System.out.println("복사\n"+buf2);
buf.position(3);
System.out.println("원본 pos를 3으로 변경 :\n"+buf);
System.out.println("복사본은 ?:\n"+buf);
System.out.println("복사본 내용--");
for(int s=0;s<10;s++){
System.out.print(buf2.get(s)+",");//출력
}

System.out.println("\n\n-----------slice()로 자르기 ------");
IntBuffer buf3=buf.slice();
System.out.println("자르기\n"+buf3);
buf.position(3);
System.out.println("\n원본 pos를 3으로 변경 :\n"+buf);
System.out.println("자른 pos은 ?:\n"+buf3);
System.out.println("자른본 내용--");
while(buf3.hasRemaining()){
System.out.print(buf3.get()+",");//출력
}

System.out.println("\n\n-----------원본 데이터를 바꾼다. ------");
buf.clear();
for(int s=0;s<5;s++){
buf.put(s);//0에서 4까지 버퍼에 저장
}
System.out.println("\n바뀐 원본 : ");
for(int s=0;s<10;s++){
System.out.print(buf.get(s)+",");//출력
}

buf2.clear();
System.out.println("\n복사본은? : ");
for(int s=0;s<10;s++){
System.out.print(buf2.get(s)+",");//출력
}

buf3.clear();
System.out.println("\n자른본은? : ");
while(buf3.hasRemaining()){
System.out.print(buf3.get()+",");//출력
}
}
}

<< 실행 결과 >>


C\>java SubBuffer

-----------wrap()로 배열을 통째로 버퍼에 넣기
10,20,30,40,50,60,70,80,90,100,
넣은후 : java.nio.HeapIntBuffer[pos=10 lim=10 cap=10]

-----------array()로 반대로 버퍼를 배열로------
10,20,30,40,50,60,70,80,90,100,
넣은후 : java.nio.HeapIntBuffer[pos=10 lim=10 cap=10]

-----------compact()로 3부터 10까지 테이터를 맨앞으로------
java.nio.HeapIntBuffer[pos=7 lim=10 cap=10]
40,50,60,70,80,90,100,80,90,100,

-----------duplicate()로 복사하기-------
복사
java.nio.HeapIntBuffer[pos=7 lim=10 cap=10]
원본 pos를 3으로 변경 :
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
복사본은 ?:
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
복사본 내용--
40,50,60,70,80,90,100,80,90,100,

-----------slice()로 자르기 ------
자르기
java.nio.HeapIntBuffer[pos=0 lim=7 cap=7]
원본 pos를 3으로 변경 :
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
자른 pos은 ?:
java.nio.HeapIntBuffer[pos=0 lim=7 cap=7]
자른본 내용--
70,80,90,100,80,90,100,

-----------원본 데이터를 바꾼다. ------
바뀐 원본 :
0,1,2,3,4,90,100,80,90,100,
복사본은? :
0,1,2,3,4,90,100,80,90,100,
자른본은? :
3,4,90,100,80,90,100,

소스가 좀 길다. 그 이유는 확인을 하기위한 출력문이 많아서 그런것 뿐 이런것들을 생략하면 반으로 줄어든다. 자 하나씩 그림과 함께 살펴보자.

1. 버퍼를 wrap() 로 생성과 동시에 배열을 데이터로 put()

10
20
30
40
50
60
70
80
90
100
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=cap=10)

2. array()메서드로 버퍼에 저장된 데이터를 배열로 리턴 받는데 반드시 hasArray()가 true를 리턴해야 된다.

3. 우선 position(3)으로 버퍼의 현 위치를 3으로 설정한 다음 campact()메서드를 호출한다. 그러면 위치 3에서 limit가 10이므로 이 사이의 데이터 7개가 버퍼 맨 앞으로 이동하고 위치(pos)는 7로 이동, limit는 capacity와 같은 값을 가진다.

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
(pos=7)
(limit=cap=10)

4. duplicate() 메서드를 호출, 버퍼를 복사해서 새로운 버퍼를 buf2에 담는다. 이때 pos/limit/cap 값들이 그대로 복사됨을 알 수 있다.

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
<- buf(원본)
(pos=7)
(limit=cap=10)

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
<- buf2(복사본)
(pos=7)
(limit=cap=10)

5. 복사를 하고 나서 원본인 buf의 position을 3으로 바꾸어 보니 복사본인 buf2도 position가 3으로 바뀐것을 확인 할 수 있다.

6. 이번에도 버퍼를 하나 더 생성하는데 slice()를 이용해서 새로 생긴 버퍼를 buf3에 담든다.그럼 지금 원본인 buf의 pos가 3이고 limit가 10이므로 이 범위의 데이타로 buf3를 생성하므로 다음과 같은 그림이다.

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
<- buf(원본)
(pos=3)
(limit=cap=10)

70
80
90
100
80
90
100
0
1
2
3
4
5
6
<- buf3(자른본)
(pos=0)
(limit=cap=7)

7. 역시 여기서도 원본인 buf의 position을 3으로 바꾸었다. 하지만 복사본과는 달리 자르기를 한 buf3는 전혀 변화가 없음을 알 수 있다.

8. 이번에는 원본의 데이터를 바꾸면 복사본과 자른 버퍼의 데이터도 바뀌는 지를 알아본다. 우선 원본인 buf의 데이터의 위치 0번부터 5번까지 데이터를 0에서 4로 바꾸었다. 그리고 나서 복사본과 자른본의 데이터를 출력하니 역시 데이터가 바뀐것을 알 수있다. 이는 복사본과 자른본은 원본과 데이터를 공유한다는 것을 알 수있다. 하지만 복사본은 원본과 pos나 limit값들도 함께 공유하지만 자른본은 데이터만 공유함을 알 수 있다.

조금 복잡해 보이나 그림을 그려가면서 따져보면 이해할 수 있다. 자 그럼 버퍼류클래스들을 살펴볼텐데 대부분이 같은 기능을 가진다. 그중에서 추가 기능을 가진 ByteBuffer와 CharBuffer에 대해서 알아보자.

6. ByteBuffer 클래스

우선 api문서에 있는 ByteBuffer 클래스의 선언은 다음과 같다.


public abstract class ByteBuffer extends Buffer implements Comparable

선언부분을 보면 ByteBuffer 클래스는 Buffer 클래스를 상속하고 Comparable 인터페이스를 구현하고 있다. Buffer 클래스를 상속는 누누히 말해 온 것이고 Comparable 인터페이스를 구현한다는 것은 ByteBuffer 클래스가 compareTo()라는 메서드를 가지고 있기 때문이다. 이는 다른 Buffer 클래스의 하위 클래스도 다 마찬가지이다.

ByteBuffer 클래스의 특징은 다음과 같이 두가지로 들수 있다.

  1. Direct Buffer를 만들 수 있다.
  2. 다른 유형의 버퍼로 변환이 가능하다.

이는 사실 하나로 생각될 수 있는데 그것은 ByteBuffer 클래스가 Direct Buffer를 만들면 이 Direct Buffer를 다른 유형으로 변환해서 쓸 수 있기 때문이다.

1> ByteBuffer 클래스 추가 메서드

ㅁ public static ByteBuffer allocateDirect(int capacity)
: Direct Buffer를 생성한다. Direct Buffer는 자바가 제공하는 Heap 메모리를 쓰지 않고 운영체제가 제공하는 메모리를 사용하므로 만들때에는 시간이 걸린다. 하지만 입출력 속도가 매우 빠른 장점을 가지고 있다. 따라서 Direct Buffer는 대용량의 데이터를 다루고 오랜 기간동안 쓰면서 빠른속도가 필요한 곳에 적합하다. 또 다른 장점으로는 JNI를 통해 직접 접근이 가능하다. JNI는 native 코드이므로 Direct Buffer를 navtive 코드에 쓰거나 native 에서 수정한 코드를 자바에서 쓰는 것이 가능하다.

ㅁ 다른 유형으로 변환시키는 메서드 -> asXXXBuffer()

public abstract CharBuffer asCharBuffer()
public abstract DoubleBuffer asDoubleBuffer()
public abstract FloatBuffer asFloatBuffer()
public abstract IntBuffer asIntBuffer()
public abstract LongBuffer asLongBuffer()
public abstract ShortBuffer asShortBuffer()

이들 메서드로 변환된 ByteBuffer는 변환유형에 맞는 Buffer로서의 기능을 할 수 있게 된다.

ㅁ public final ByteOrder order()
: 해당 버퍼의 ByteOrder를 리턴

ㅁ public final ByteBuffer order(ByteOrder bo)
:
인자로 들어온 ByteOrder로 버퍼를 설정한다. 인자로는 바이트 순서인 BIG_ENDIAN 또는 LITTLE_ENDIAN 이 온다.
: 다른 클래스에서도 ByteOrder(바이트 순서)를 변경할 수 있는데, 이는 직접 변경할 수는 없고 ByteBuffer클래스에서 바꾼 다음 이른 변환해야 한다.

ㅁ getXXX()/putXXX()
: ByteBuffer는 여러 유형의 데이터를 put/get 할 수 있는 메서드가 있다. put/get 뒤에 해당 데이터형만 적으면 된다. 이는 ByteBuffer가 다른 유형의 버퍼로 전환될 수 있는 능력을 가지고 있기 때문이다. 예를 들어 다음과 같이 사용한다.

buf1.putChar('가');
buf1.putDouble(3.14);
buf1.putFloat(3.14f);

이를 다음과 같이 호출해도 상관없다.

buf1.putChar('가').putDouble(3.14).putFloat((float)3.14);

ㅁ public int compareTo(Object ob)
: 인자로 들어온 객체 ob와 비교해서 결과 값을 리턴해 준다. 이때 리턴값이 음수면 객체 ob보다 자신이 작다는 뜻이고, 0은 같다는 뜻, 양수는 객체 ob보다 자신이 크다는 뜻이다. 주의할 점은 반드시 유형이 같은 버퍼끼리 비교해야 한다.

2> 예제


import java.nio.*;

public class ByteBufferTest {
public static void main(String[] args) {
// 버퍼를 생성한다.
ByteBuffer buf1=ByteBuffer.allocate(25);
// ByteOder(데이터 순서)를 출력한다.
System.out.println("Byte order : "+buf1.order());
// 여러 유형의 데이터를 put한다.
buf1.putChar('가');
buf1.putDouble(3.14);
buf1.putFloat((float)3.14);
// buf1.putChar('가').putDouble(3.14).putFloat((float)3.14); 라고 해도 된다.

System.out.println("buf1 : "+buf1);

// 여러 유형의 데이터를 get한다.
System.out.println(buf1.getChar());
System.out.println(buf1.getDouble());
System.out.println(buf1.getFloat());

//새로운 버퍼 생성
ByteBuffer buf2=ByteBuffer.allocate(20);
// 데이터 int형을 put
for(int i=1;i<=5;i++){
buf2.putInt(i);
}
// 버퍼의 위치값을 0으로 설정
buf2.clear();
// ByteBuffer를 IntBuffer로 변환시킨다.
IntBuffer intbuf=buf2.asIntBuffer();

System.out.println("IntBuffer로 변환후 값은:"+intbuf);

while(intbuf.hasRemaining()){
System.out.println(intbuf.get());//출력

}

}
}

<< 실행 결과 >>


C\>java ByteBufferTest

Byte order : BIG_ENDIAN
buf1 : java.nio.HeapByteBuffer[pos=14 lim=25 cap=25]

3.14
3.14
IntBuffer로 변환후 값은:java.nio.ByteBufferAsIntBufferB[pos=0 lim=5 cap=5]
1
2
3
4
5

소스를 보면 ByteBuffer 를 생성하고 이것의 ByteOrder를 출력해보니 BIG_ENDIAN임을 확인할 수 있다. 이것은 운영체계와 별개의 값이다. 자바가상머신은 하나의 독립된 컴퓨터이기 때문이다. 그리고 char,double,float 형의 데이터를 putXXX()메서드로 넣고 있다. 그리고 나서 다시 get을 해서 출력한다.

다시 크기가 20인 바이트 버퍼를 생성하고 이번에는 int형의 데이터를 삽입했다. 그리고 나서 버퍼의 위치를 clear()메서드로 0으로 만들고 나서 ByteBuffer를 IntBuffer로 변환한다. 변환하기전에 반드시 버퍼의 위치를 0으로 만들주어야 한다. 그이유는 변환시 버퍼의 현재 위치부터 변환이 시작되기 때문이다. 변환된 객체 intbuf를 출력하면 position는 0이 되고 limit와 capacity가 5으로 바뀐것을 볼 수 있다. 이는 IntBuffer로 변환되면서 데이터 크기를 int형의 크기로 바꾸어 버리기 때문이다. 즉 총 크기가 20이니 int는 4바이트를 차지하니깐 이를 나누면 5이 나온다. 하지만 데이터는 int형이 아니라서 그 크기에 맞게 들어간 것이 보인다.

7. CharBuffer 클래스

CharBuffer는 char 데이터 유형을 대상하는 버퍼로 특징은 1.4부터 추가된 java.lang.CharSequence 인터페이스를 구현하고 있다는 점이다. 다음은 CharBuffer의 클래스 선언문이다.


public abstract class CharBuffer extends Buffer implements Comparable , CharSequence

CharSequence 인터페이스를 구현하기 때문에, 문자 순서를 받아들일 수 있는 장소이면 어디에서라도, char 버퍼를 사용할 수 있다. 예를 들어, 1.4부터 추가된 정규 표현의 패키지 java.util.regex 에서의 사용이 가능하다. 또한 wrap()메서드에서 char[] 배열 대신에 CharSequence 객체가 들어 올 수 있어서 String이나 StringBuffer를 wrap()해서 CharBuffer로 만들 수 있다.

CharSequence 인터페이스

JDK 1.4부터 추가된 인터페이스로서 CharSequence 는 읽어들일 수 있는 문자 순서를 나타낸다. 이 인터페이스는, 다양한 종류의 문자 순서에의 통일된 read 전용 액세스를 제공한다.

<< 메서드 >>
ㅁ char charAt (int index) : 지정된 인덱스 위치에 있는 문자를 리턴
ㅁ int length () : 이 문자 순서의 길이를 리턴.
ㅁ CharSequence subSequence (int start, int end) : 이 순서의 서브 순서인 신규 문자 순서를 리턴.
ㅁ String toString () : 이 순서와 같은 순서로 문자를 포함한 스트링을 리턴.

구현 클래스 : CharBuffer , String , StringBuffer

모든 버퍼류 클래스의 객체들은 toString()메서드 호출시 자신의 여러 설정들을 보여준다. 가령 예를 들어 다음과 같이 코드를 작성해서 실행을 했다고 하자.

IntBuffer buf=IntBuffer.allocate(10);
System.out.println("생성된 후:"+buf);//자동 toString() 호출....당연,,,

==>실행 결과
생성된 후 : java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]

하지만 CharBuffer는 자신이 가지고 있는 char들을 보여준다. 따라서 CharBuffer의 여러 값들을 보고자 할때에는 메서드를 사용해야 한다.CharBuffer에는 위같은 사실만 알고 있으면 사용하는데는 별 무리가 없다.

1> 예제


import java.nio.*;

public class CharBufferTest {
public static void main(String[] args) {

// CharBuffer에 String 문자열로 wrap한다.
String s="String 문자열";
CharBuffer buf=CharBuffer.wrap(s);
// 버퍼의 값들을 알기 위해서는 다른 클래스와는 달리 메서드를 사용해야 한다.
System.out.println("pos:"+buf.position()+" limit:"+buf.limit()+" cap:"+buf.capacity());
buf.clear();
while(buf.hasRemaining()){
System.out.print(buf.get());//출력
}

System.out.println("\n"+"-------------------------");

// CharBuffer에 StringBuffer 문자열로 wrap한다.
StringBuffer s2 = new StringBuffer("StringBuffer 문자열");
CharBuffer buf2=CharBuffer.wrap(s2);
System.out.println("pos:"+buf2.position()+" limit:"+buf2.limit()+" cap:"+buf2.capacity());
buf2.clear();
while(buf2.hasRemaining()){
System.out.print(buf2.get());
//출력
}
}
}

<< 실행 결과 >>


C\>java CharBufferTest

pos:0 limit:10 cap:10
String 문자열
-------------------------
pos:0 limit:16 cap:16
StringBuffer 문자열

소스를 보면 두 개의 CharBuffer를 생성해서 하나는 String 을 데이터로, StringBuffer를 데이터로 삽입한다. 이것이 가능한 이유는 앞서 말했지만 CharSequence 인터페이스를 구현하기 때문이다. 그리고 CharBuffer의 객체를 출력시 값을 알기위해서 다음과 같이 출력함을 볼 수 있다.

System.out.println("pos:"+buf.position()+" limit:"+buf.limit()+" cap:"+buf.capacity());

[출처] Nio Buffer정리[펌]|작성자 짱가

 

.
:
Posted by .07274.
2013. 4. 19. 10:53

겔럭시 s 부두 패치 경로 I.lib()/I.lib(etc)2013. 4. 19. 10:53

.. .. ..

http://cafe.naver.com/iroid/786690

'I.lib() > I.lib(etc)' 카테고리의 다른 글

부산 여행 Tip  (0) 2013.08.09
PMD Rule Set 정리 [펌]  (0) 2013.07.30
[펌] TDD  (0) 2013.03.28
캐리커쳐 그리기 쉬운 사이트  (0) 2012.10.25
7만번대 ps2 하드 플스 개조 (펌)  (0) 2011.08.04
.
:
Posted by .07274.
2013. 4. 18. 14:56

[펌] 제한자 정리 I.lib()/I.lib(Java)2013. 4. 18. 14:56

.. .. ..

[출처] 제 2장 6.7. native/ transient/ synchronized/ volatile 제한자와 위치|작성자 멋진놈

 

이절에서 소개하는 제한자는 자주 사용되지는 않는다. 하지만 이 제한자들이 어떤 용도로 사용되는지에 대해서는 알으시면 될것 같습니다. 이 제한자들 또한 자바 키워드임을 명심하자.

native 제한자
- native 제한자(modifier)는 자바가 아닌 다른 언어로 작성된 코드를 자바에서 사용하기 위한 것이다.
이제한자는 반드시 메소드에만 선언되어야 한다.

transient 제한자
- transient 제한자는 객체가 직렬화되는 과정에서 해당 필드가 저장되지 않아야 한다는 것을 알리기
위해 사용된다. 이 제한자는 반드시 멤버 변수에만 선언되어야 한다.

synchronized 제한자
- synchronized 제한자는 코드의 같은 블록을 하나 이상의 스레드가 동시에 접근하는 것을 막기 위
해 사용된다. 이 제한자는 반드시 메소드나 블록에 선언되어야 한다.

volatile 제한자
- volatile 제한자는 변수가 스레드에 의해 비동기적으로 변경될 수 있음을 알리기 위해 사용된다. 이
제한자는 final 변수를 제외한 변수에 선언될 수 있다.

제한자들은 선언될 수 있는 위치가 정해져 있기 때문에 선언 기능 위치에 대해서 정확히 알고 선언하는 것이 중요하다.
제한자
변수
메소드
클래스
내부 클래스
public
private
×
protected
×
final
abstract
×
static
×
native
×
×
×
transient
×
×
×
volatile
×
×
×
synchronized
×
×
×


※ 제한자의 선언 위치

 

.
:
Posted by .07274.
2013. 4. 9. 11:48

[펌] Log4j 설정 (xml 파일) I.lib()/I.lib(Java)2013. 4. 9. 11:48

.. .. ..

[펌] : http://rahxephon.tistory.com/1460

Logging Service

개발자가 Log을 출력하기 위해 일반적으로 사용하는 방식은 System.out.println()이다. 그러나 이 방식은 간편한 반면에 다음과 같은 이유로 권장하지 않는다.

  • System.out.println에 대한 호출은 disk I/O동안 동기화(synchronized)처리가 되므로 시스템의 throughput을 떨어뜨린다.
  • 기본적으로 stack trace 결과는 콘솔에 남는다. 하지만 시스템 운영중 콘솔을 통해 Exception을 추적하는 것은 바람직하지 못하다.
  • 운영시스템에서 시스템 관리자가 System.out과 system.errs에 대하여 ‘[>null]’(NT의 경우) 혹은 dev/null(Unix의 경우)와 같이 매핑을 할 경우 Exception 로그에 대한 출력이 나타나지 않을 수도 있다. 또한 NT 서비스로 실행될 경우 콘솔 자체가 안보일 수 도 있다.
  • 콘솔 로그를 출력 파일로 리다이렉트 할 지라도, J2EE App Server가 재 시작할 때 파일이 overwrite될 수도 있다.
  • 개발/테스팅 시점에만 System.out.println을 사용하고 운영으로 이관하기 전에 삭제하는 것은 좋은 방법이 아니다. 운영시의 코드가 테스트시의 코드와 다르게 동작할 수 있기 때문이다.

따라서, 테스팅 코드와 운영 코드를 동일하게 가져가면서 로깅을 선언적으로 관리할 수 있고, 운영시 성능 오버헤드를 최소화할 수 있는 메커니즘이 필요하다. 이런 기능을 위해 Anyframe Framework은 Log4j 를 이용하여 로그를 남길 수 있는 방법을 가이드하고자 한다.

Logging Service Configuration 정의하기

이번 절에서는 log4j.xml 파일을 구성하는 Tag 중, 주로 사용될 일부 Tag에 대해 설명하고자 한다. 보다 자세한 내용에 대해서는 Log4j 를 참조하도록 한다. log4j.xml 파일의 root tag인 <log4j:configuration>은 하위에 appender, logger, root등의 tag를 가질 수 있다.
Tag 명
설명
필수 여부
appender 로그가 출력될 대상과 방법을 정의한다. 여러 appender 정의 가능.
N
logger 어플리케이션에서 사용될 Logger를 정의한다. 여러 logger 정의 가능
N
root 모든 logger의 상위 logger를 정의한다.
N

위 표에서 열거한 각 Tag에 대해 보다 자세히 알아보도록 하자.

appender

Log4j는 다양한 로그 방식을 지원한다. 가장 단순한 Console부터 시작해서 파일, DB, SMTP 등의 방식들을 지원한다.
  • org.apache.log4j.ConsoleAppender : Console 화면으로 출력하기 위한 Appender. 다음은 log4j.xml 파일 내의 ConsoleAppender에 대한 속성 정의 내용이다.
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.FileAppender : 특정 파일에 로그를 출력하기 위한 Appender로 하위에 File, Append와 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 FileAppender에 대한 속성 정의 내용이다.
    <appender name="file" class="org.apache.log4j.FileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/file/sample.log"/>
    	<!-- 이전 로그 파일에 로그를 덧붙여 쓸 것인지를 정의하기 위한 parameter -->
    	<param name="Append" value="true"/>    	  
    	<layout class="org.apache.log4j.PatternLayout">
    	    <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
    	</layout>   
    </appender>
  • org.apache.log4j.RollingFileAppender : FileAppender는 지정한 파일에 로그가 계속 남으므로 한 파일의 크기가 지나치게 커질 수 있으며 계획적인 로그 관리가 어렵다. 따라서 파일의 크기 또는 파일 백업 인덱스 등의 지정을 통해 특정 크기 이상 파일의 크기가 커지게 되면 기존 파일을 백업 파일로 바꾸고 처음부터 다시 로깅을 시작한다. 하위에 File, Append, MaxFileSize, MaxBackupIndex와 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 RollingFileAppender에 대한 속성 정의 내용이다.
    <appender name="rollingFile" class="org.apache.log4j.RollingFileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/rolling/sample.log"/>
    	<!-- 이전 로그 파일에 로그를 덧붙여 쓸 것인지를 정의하기 위한 parameter -->
    	<param name="Append" value="true"/> 
    	<!-- 로그 파일의 최대 크기를 정의하기 위한 parameter -->   	  
    	<param name="MaxFileSize" value="1KB"/>    
    	<!-- 로그 파일 백업 인덱스를 정의하기 위한 parameter -->
    	<param name="MaxBackupIndex" value="2"/>    
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.DailyRollingFileAppender : 설정한 날짜 또는 조건에 맞춰 로깅을 수행하기 위한 Appender. DailyRollingFileAppender 파일에서 사용할 수 있는 몇가지 날짜 포맷은 다음과 같다. 하위에 File, DatePattern과 같은 parameter를 정의할 수 있다.
    • .yyyy-MM : 매달 첫번째 날에 로그파일 변경
    • .yyyy-ww : 매주 시작시 로그파일 변경
    • .yyyy-MM-dd : 매일 자정에 로그파일 변경
    • .yyyy-MM-dd-a : 자정과 정오에 로그파일 변경
    • .yyyy-MM-dd-HH : 매시간의 시작마다 로그파일 변경
    • .yyyy-MM-dd-HH-mm : 매분마다 로그파일 변경
    보다 자세한 사항은 Log4j API 를 참조한다.
    다음은 log4j.xml 파일 내의 DailyRollingFileAppender에 대한 속성 정의 내용이다.
    <appender name="dailyRollingFile" class="org.apache.log4j.DailyRollingFileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/daily/sample.log"/>
    	<!-- 로그 파일을 Rolling하기 위한 날짜 조건을 정의하기 위한 parameter -->
    	<param name="DatePattern" value=".yyyy-MM-dd-HH-mm"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.jdbc.JDBCAppender : DB에 로그를 출력하기 위한 Appender로 하위에 Driver, URL, User, Password, Sql과 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 JDBCAppender에 대한 속성 정의 내용이다.
    <appender name="db" class="org.apache.log4j.jdbc.JDBCAppender">
    	<!-- JDBC Driver를 정의하기 위한 parameter -->
    	<param name="Driver" value="oracle.jdbc.driver.OracleDriver"/>
    	<!-- DB URL을 정의하기 위한 parameter -->
    	<param name="URL" value="jdbc:oracle:thin:@107.108.150.108:1521:ora10"/>
    	<!-- DB User를 정의하기 위한 parameter -->
    	<param name="User" value="anyframe"/>
    	<!-- DB Password를 정의하기 위한 parameter -->
    	<param name="Password" value="anyframe"/>
    	<!-- 로그를 남길때 수행할 쿼리를 정의하기 위한 parameter -->
    	<param name="Sql" value="insert into STMR_LOG (msg)
    	                        values('%d %p [%c] - &lt;%m&gt;%n')"/>
    </appender>
Appender Layout
로그를 남길때 단순한 메시지 외에도 현재 로그 대상의 쓰레드명, 로그 시간 등 많은 정보들을 조합할 수 있다. Layout에는 org.apache.log4j.HTMLLayout, org.apache.log4j.PatternLayout, org.apache.log4j.SimpleLayout, org.apache.log4j.xml.XMLLayout 등이 있다. 이중 가장 많이 사용하는 Layout은 PatternLayout으로서 C 함수의 printf처럼 다양한 로그 메시지 조합을 만들어 낼 수 있다.
  • %p : debug, infor, warn, error, fatal 등의 Priority 출력
  • %m : debug(), info(), warn(), error(), fatal() 등의 함수로 지정한 로그 내용 출력
  • %d : 로깅 이벤트가 발생한 시간 기록. 출력 포맷은 %d 후의 {}내에 지정된 형태를 따른다. %d{HH:mm:ss, SSS} 라든가 %d{yyyy MMM dd HH:mm:ss, SSS}와 같은 형태로 사용할 수 있다. Java의 SimpleDateFormat의 형식을 따라 정의할 수 있다.
  • %t : 로깅 이벤트가 발생된 쓰레드의 이름 출력
  • %% : % 표시를 출력하기 위해 사용
  • %n : 플랫폼 종속적인 개행 문자 출력. \r\n 또는 \n이 될 것이다.

logger

로깅 이벤트 발생시 같은 이름으로 선언된 logger를 찾아 해당 logger에게 로그 메시지를 보내고 additivity가 true일 경우, 상위 logger에게도 로그 메시지를 보낸다. 다음은 log4j.xml 파일 내의 logger에 대한 속성 정의 내용이다.
<!-- 해당 logger명이 anyframe.services로 시작할 경우 Console에 DEBUG level로 로그를 남긴다.-->
<logger name="anyframe.services">
	<!-- DEBUG, INFO, WARN, ERROR, FATAL, OFF 중 택일 -->
	<level value="DEBUG"/>
	<!-- 여러 appender-ref 정의 가능 -->
	<appender-ref ref="console"/>
</logger>

root

해당 logger가 존재하지 않거나 상위 logger가 존재하지 않을 경우 모든 로그는 root logger의 정책에 따라 출력된다. 다음은 log4j.xml 파일 내의 root에 대한 속성 정의 내용이다.
<root>
    <level value="INFO"/>
    <appender-ref ref="console"/>
</root>

Logging Service 사용하기

로그의 내용에 따라 다양한 레벨(DEBUG, INFO, WARN, ERROR, FATAL)로 선택 가능하다. 각각은 메소드 debug(), info(), warn(), error(), fatao()라는 5가지 메소드를 이용해서 로그를 남길 수 있다. 다만 이때 Logger에 지정된 로그 레벨이 있다면, 지정된 로그 레벨 이하의 로깅 이벤트는 무시된다. 따라서 로그도 남지 않는다. 또한, 로그 메시지는 별도 Resource 파일에 정의된 message key를 이용하여 남기면 메시지 변경 및 다국어 지원이 용이하다. 다음에서는 로그 메시지를 남기기 위한 기본 방법ResourceBundle을 이용하는 방법 에 대해서 알아보고자 한다.

기본적인 사용 방법

다음은 기본적인 방법을 사용하여 로그 메시지를 남기는 LoggingServiceTest.java 코드의 일부이다.
/**
 * resources/test/resources/common/conf/log4j.xml 파일 설정에 따라 Logger명이
 * integration.anyframe.services.logging.LoggingServiceTest인 Logger를 찾고, 해당
 * Logger를 통해 로그 메시지를 남겨보고 해당 Logger의 레벨이 DEBUG 가능한지 체크해보는 테스트
 */
public void testLogging() throws Exception {
	Log logger = LogFactory.getLog(LoggingServiceTest.class);
	logger.debug("Sample DEBUG Message.");
	logger.info("Sample Info Message.");
	logger.warn("Sample Warning Message.");
	logger.error("Sample Error Message.");
	logger.fatal("Sample Fatal Message.");
	if (!logger.isDebugEnabled())
		throw new Exception("fail to get Logger");
}

ResourceBundle을 이용하는 방법

특정 서비스의 구현 클래스에서 ResourceBundle을 이용하여 로그 메시지를 남기기 위해서는 다음과 같은 절차를 따르도록 한다.
1. ResourceBundle을 관리하는 기능을 제공하는 MessageSource Bean을 정의한다.
다음은 MessageSource Bean을 정의하고 있는 applicationContext-common.xml 파일의 일부이다.
<bean name="messageSource" 
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <!-- 중략 -->
            <value>
               classpath:/message/message-sample
            </value>
        </list>
        <!-- 중략 -->
    </property>
</bean>
2. 특정 서비스의 구현 클래스는 MessageSource Bean을 인식하기 위하여 implements ApplicationContextAware해야 한다.
다음은 MessageSource Bean을 이용하여 로그 메시지를 남기는 LoggingSampleServiceImpl.java 의 일부 코드이다.
public class LoggingSampleServiceImpl implements LoggingSampleService,
		ApplicationContextAware {
	private MessageSource messageSource = null;
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.messageSource = (MessageSource) applicationContext
				.getBean("messageSource");
	}
	
	// 중략
}
3. Logging Service를 이용하여 로그를 남길 때 MessageSource Bean을 이용한다.
다음은 Service 구현 클래스에서 사용할 Logger를 정의한 인터페이스 클래스 LoggingSampleService.java 코드이다.
public interface LoggingSampleService {
	String ROLE = LoggingSampleService.class.getName();
	// LoggingSampleServiceImpl에서 사용할 Logger 정의
	Log LOGGER = LogFactory.getLog(LoggingSampleService.class);
	public String greet();
}
다음은 ResourceBundle을 이용하여 로그 메시지를 남기는 구현 클래스 LoggingSampleServiceImpl.java 의 일부 코드이다.
public String greet() {
	// ResourceBundle을 이용하여 로그 메시지를 남긴다. (argument가 없는 경우)
	LoggingSampleService.LOGGER.debug(messageSource.getMessage(
			"sample.default.msg", new String[] {}, Locale.getDefault()));
	// ResourceBundle을 이용하여 로그 메시지를 남긴다. (argument가 1개인 경우)
	LoggingSampleService.LOGGER.debug(messageSource.getMessage(
			"sample.msg", new String[] { "GilDong" }, Locale.getDefault()));
	return "Hello";
}
* 위의 코드에서 참조하고 있는 Resource 파일 message-sample.properties 의 내용은 다음과 같이 key=value 형태로 정의한다.
sample.default.msg=Hello Guest
sample.msg=Hello {0}
.
:
Posted by .07274.
2013. 4. 1. 15:45

Mybatis 설정 I.lib()/I.lib(iBatis)2013. 4. 1. 15:45

.. .. ..

여기서 눈여겨 볼 사항이 <property name="poolPingQuery" value="select 1"/> 이다.

기존에 validationQuery 대신 poolPingQuery로 사용 하고 있다.


다음은 각 pool 과 관련된 속성 값 설명이다.

<property name="poolMaximumActiveConnections" value="20"/>

: 동시 활성화 할 커넥션 수


<property name="poolMaximumIdleConnections" value="20"/>

: 유휴상태의 커넥션 수


<property name="poolMaximumCheckoutTime" value="20000"/>

: 커넥션 요청 후 획득까지 기다리는 시간


<property name="poolPingEnabled" value="true"/>

: 커넥션 ping 테스트


<property name="poolPingQuery" value="select 1"/>

: 커넥션이 살아 있는지 확인할 쿼리


<property name="poolPingConnectionsNotUsedFor" value="43200"/>
: 커넥션이 얼마 동안 유휴상태면 닫히는지 판단하는 시간


<property name="poolTimeToWait" value="30000"/>

: 사용 불가능한 커넥션 기다리는 시간


<property name="poolPingConnectionsOlderThan" value="43200"/>

: 어떤 커넥션이 닫힐 상태인지 판단하는 기준시간

.
:
Posted by .07274.
2013. 3. 28. 15:53

[펌] TDD I.lib()/I.lib(etc)2013. 3. 28. 15:53

.. .. ..

[펌] : http://soulpark.wordpress.com/2012/09/12/test-driven-development/

 

Test-Driven Development

린(Lean) 소프트웨어 개발론의 핵심 철학 중 하나는 “결함은 발견 즉시 해결”이다. 린 개발은 이것의 실천법으로 테스트 주도 개발(Test-Driven Development, TDD)을 제시한다. TDD 의 개념과 방법론 그리고 한계에 대해 알아보자.

TDD (Test-Driven Development) 란?

TDD는 반복 테스트을 이용한  소프트웨어 개발법이다. 작은 단위의 테스트 케이스를 작성하고 이를 통과하는 코드를 추가하는 단계를 반복하여 소프트웨어를 구현한다.

TDD의 목표는 작동하는 깔끔한 코드 “Clean code that works” 이다. 이를 위해 두 가지 원칙을 제시한다 .

  • 오직 자동화된 테스트가 실패할 경우에만 새로운 코드를 작성한다.
  • 중복을 제거한다.

린에서는 불필요한 기능 구현을 가장 큰 낭비로 여긴다. 이는 코드베이스의 복잡성을 높이고, 유지 보수를 어렵게 하여 높은 개발 비용을 초래한다. TDD도 테스트를 통과시키는 코드만을 작성함으로써 맥락을 같이한다.

요구사항 분석 -> 설계 -> 개발 -> 테스트 -> 배포 개발법엔 소프트웨어 개발을 느리게 하는 잠재적 위험이 존재한다.

  • 소비자의 요구사항이 처음부터 명확하지 않을 수 있다.
  • 따라서 처음부터 완벽한 설계는 어렵다.
  • 설계 -> 개발을 진행하면, 실제 코드와 설계간 갈등이 생길 수 있다.
  • 한 곳의 수정이 다른 곳에 미치는 영향을 확인 및 다른 기능의 정상 동작을 보장하기 어렵다.

TDD는 테스트 케이스를 생성한다. 테스트 케이스는 자동화된 테스트 도구로 이용되어, 코드 변경시 기존 기능이 제대로 동작하는지 쉽게 확인할 수 있고 정상 동작을 보장한다. 또한 TDD는 리펙토링을 개발 프로세스에 포함시켜 ‘변경’이라는 소프트웨어의 특성을 반영한다.

애자일 개발론의 Robert C. Martin은 다음의 TDD 원칙을 제시한다.

  • 실패하는 테스트를 작성하기 전에는 절대로 제품 코드를 작성하지 않는다.
  • 실패하는 테스트 코드를 한 번에 하나 이상 작성하지 않는다.
  • 현재 실패하고 있는 테스트를 통과하기에 충분한 정도를 넘어서는 제품 코드를 작성하지 않는다.

TDD 개발법

TDD는 아래 단계의 반복으로 진행된다.

  1. 빨강 : 실패하는 작은 테스트 케이스를 작성한다. 처음에는 컴파일조차 안될 수 있다.
  2. 초록 : 테스트를 통과하는 코드를 작성한다.
  3. 리펙터링 : 테스트를 통과하기 위해 만든 코드의 모든 중복을 제거하고, 불명확한 것을 명확히 한다.

이러한 단계로 인해 TDD는  ”업무 코드 작성 전에 테스트 코드를 먼저 만드는 것”으로 정의되기도 한다. 이해를 위해 간단한 sum 함수를 예제로 살펴보자.

  • 작성 메소드 이름 : sum
  • 기능 구현에 필요한 재료 (argument) : int a , int b
  • 반환 값의 타입 : int
  • 정상 동작 만족 조건 (작업 종료 조건) : a와 b 를 더한 값을 결과로 돌려줌

간단히 작성된 sum 함수의 테스트 조건이다. 꼭 위와 같은 형식이 아니라 하나의 문장이어도 된다. 단 테스트는 너무 크지 않은 규모여야 하며 테스트 통과 조건(작업 종료 조건)이 반드시 있어야 한다.

아래 예시 코드를 보자. (제품 코드 관리를 위해 테스트 클래스는 제품 클래스와 구분되어어 쌍을 이루는 것이 일반적이지만, 간단한 예시를 위해 하나의 클래스 안에 테스트 메소드와 제품 메소드를 같이 두었다.)

1
2
3
4
5
6
7
8
9
10
11
-(void)testMain{ // 테스트 메소드
 
    NSLog(@"True or False : %d",[self sumWithIntegers:10 :20] == 30);
    NSLog(@"True or False : %d",[self sumWithIntegers:1 :2] == 3);
    NSLog(@"True or False : %d",[self sumWithIntegers:-10 :20] == 10);
    NSLog(@"True or False : %d",[self sumWithIntegers:0 :0] == 0);
 
}
-(int)sumWithInteger:(int)a <img src="http://s0.wp.com/wp-includes/images/smilies/icon_sad.gif?m=1129645325g" alt=":(" class="wp-smiley"> int)b{ // 제품 메소드
    return 0;
}

먼저 테스트 메소드를 작성한다. 메소드 안에는 테스트 성공 여부를 확인할 수 있는 코드가 있다. 위 예제는 로그에 모두 True 가 나오면 테스트 케이스가 통과되었다고 가정한다.

제품 메소드보다 테스트 메소드를 먼저 작성하므로 sumWithIntegers: 메소드를 찾을 수 없다는 컴파일 에러가 발생한다. 의아해 할 필요없다. 의도한 바다. 테스트 코드를 작성하면서 자연스럽게 제품 코드의 클래스 이름 및 메소드 이름 등의 설계 요소를 고민하게 된다.

다음 단계로 테스트 케이스의 결과를 ‘통과’로 만든다. 컴파일 오류 해결을 위해 sumWithIntegers: 메소드를 정의한다. 그리고 메소드 안에 테스트를 만족시키는 코드를 작성한다. (예시에서는 단순히 0을 리턴하였다. 제품 코드를 먼저 작성하지 않음을 보여주기 위함이다.)

테스트가 통과되면 테스트 수행에 사용된 테스트 클래스와 제품 클래스 모두 리펙토링한다. 필드 이름, 중복 제거, 가독성 향상 등 간결한 코드를 유지하여 변경에 유연한 코드를 만들도록 노력한다. 리펙터링은 이전의 테스트 케이스를 모두 통과시켜야만 한다. 개발 단계에서  테스트 케이스가 누적되어 유지되므로 이전 기능들에 대한 정상 동작 여부가 보장된다. (위 예시는 지극히 간단하여 리펙토링 단계 없음)

개인적으로 다음의 역량이 TDD 개발에 있어 개발자에게 중요할 것 같다.

  • 테스트 케이스 작성 능력 : 테스트 케이스 자체가 의미있지 않으면 개발된 코드를 신뢰할 수 없다.
  • API 숙련도 : 테스트 케이스를 먼저 작성하면 컴파일 조차 되지 않아 개발 도구의 자동 완성 기능의 도움을 받지 못할 수 있다.
  • 설계 및 리펙토링 능력 : 매 단계마다 상황에 맞는 설계적 결정을 해야 한다. 전체 SW 구조에 대한 직관을 가질 필요가 있다.

테스트 케이스가 의미있으려면 테스트 케이스는 정상적으로 끝까지 수행되어야 한다. 테스트의 결과 중 개발자가 의도 하지 않은 문제 – 예상치 못한 Exception 등 – 는 실패가 아닌 오류로 기록된다. 테스트 케이스가 정상적으로 끝까지 진행되어 오류가 아닌 실패로 기록되도록 하자.

테스트 클래스를 생성/사용할 때는 제품코드와 ‘소스 코드는 다르게, 패키지는 동일, 컴파일된 클래스는 다른 곳으로’ 설정하는 방법이 가장 많이 쓰인다. 폴더만으로 제품 코드와 분리하면 테스트만을 위한 라이브러리를 구별하기가 어려워진다.

일반적으로 TDD에는 Unit Test Framework이 많이 사용된다. iOS 개발 도구 Xcode에는 OCUnit Test Framework이 내장되어 있다.

TDD 장점

  • 개발자의 방향을 잃지 않게 유지: 현재 자신의 개발 내용 및 진척 상황을 항상 살펴볼 수 있다.
  • 품질 높은 소프트웨어 모듈 보유 : 간결한 코드 유지 가능
  • 자동화된 단위 테스트 케이스 소유: 개발자가 필요한 시점에 언제든 수행할 수 있으며 시스템의 이상 유무를 바로 확인할 수 있다.
  • 사용설명서 & 의사 소통의 수단:  작성된 테스트 케이스는 제품 코드 사용 설명서이자 동시에 다른 개발자와 소통하는 커뮤니케이션 통로가 된다. (제품 코드 API 사용 예시가 된다.)
  • 설계 개선 : 테스트 케이스 작성 시 개발에 포함된 다양한 설계요소들에 대해 고민하게 된다. 흔히 테스트하기 어렵다고 생각되는 코드들은 객체 설계 원리 중 기본에 해당하는 원칙들이 잘못 적용됐거나 충분히 고려되지 않았을 가능성이 높다. TDD 진행하면서 테스트가 가능하도록 설계 구조를 고민하다 보면 자연스럽게 디자인을 개선하게 된다.
  • 보다 자주 성공한다 : TDD 는 주기를 짧게 설정하도록 권한다. 개발자는 성취감을 자주 느낄 수 있다.

TDD 의 한계

TDD 선구자인 Kent Beck 은 다음 두 가지를  TDD가 현재 접근하기 어려운 분야라고 말한다.

  • 동시성(Concurrency)
  • 보안 등의 비기능적 요소

이 외에 국내 TDD 도서 저자인 채수원씨는 본인의 도서에서 아래와 같이 추가적인 어려움들을 덧붙였다. 불가능하진 않지만 쉽지 않다는 의미로 받아들이면 될 것 같다.

  • 접근 제한자 메소드 : 현재는 public 메소드만 테스트해도 무방하다는 경향이 대세로 보이지만, 필요에 따라 public 으로 테스트 후 접근 제한자를 수정하는 방법도 있다.
  • GUI : 뷰 레이어와 로직 레이어를 확실히 분리시켜 설계할 필요가 있다.
  • 의존성 모듈 테스트 (target = A but A -> B) : 타겟 A 테스트를 위해 B 도 필요하다. 그러나  B가 아직 준비가 되어 있지 않다면 테스트가 이루어질 수 없다. 보통 이런 경우 B의 Mock객체를 생성하여 B는 문제없음을 가정하고 테스트 한다.

* Updated 9/14 , 접근제한자 메소드 한계 : TDD 단계를 준수하면  Private 메소드는 빨강 – 초록 단계를 지나 리펙토링 단계에서 생성된다. Private 메소드는 새로운 테스트 케이스가 필요한 것이 아니라 이미 통과한 테스트의 리펙토링 결과다. 별도로 접근제한자를 변경해서 테스트할 필요가 없다. 그렇게 되도록 TDD 개발 단계를 준수하자.

* Reference

 

'I.lib() > I.lib(etc)' 카테고리의 다른 글

PMD Rule Set 정리 [펌]  (0) 2013.07.30
겔럭시 s 부두 패치 경로  (0) 2013.04.19
캐리커쳐 그리기 쉬운 사이트  (0) 2012.10.25
7만번대 ps2 하드 플스 개조 (펌)  (0) 2011.08.04
하드 플스 관련 주소  (0) 2011.04.26
.
:
Posted by .07274.
.. .. ..

[펌] : http://www.mimul.com/pebble/default/2007/12/09/1197199680000.html

 

 sqlmap.xml 정의

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<properties resource="com/mimul/dwr/app/resource/database.properties"/>
<settings
cacheModelsEnabled="true"
enhancementEnabled="true"
lazyLoadingEnabled="true"
maxRequests="40"
maxSessions="20"
maxTransactions="5"
useStatementNamespaces="false"
/>
<transactionManager type="JDBC">
<dataSource type="DBCP">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>

<!-- OPTIONAL PROPERTIES BELOW -->
<property name="initialSize" value="5"/>
<property name="maxActive" value="30"/>
<property name="maxIdle" value="20"/>
<property name="maxWait" value="60000"/>
<property name="poolPreparedStatements" value="true"/>
<property name="validationQuery" value="select 0 from dual"/>
<property name="testOnBorrow" value="true"/>
<property name="maximumActiveConnections" value="10"/>
<property name="maximumIdleConnections" value="5"/>
<property name="maximumWait" value="60000"/>
<property name="logAbandoned" value="false"/>
<property name="removeAbandoned" value="false"/>
<property name="removeAbandonedTimeout" value="50000"/>
</dataSource>
</transactionManager>
<sqlMap resource="com/mimul/dwr/app/sql/Mimul.xml"/>
</sqlMapConfig>
4. com/mimul/dwr/app/resource/database.properties 정의
driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@mimuluserdb:1521:mimuluser
username=mimuluser
password=mimuluser
5. com/mimul/dwr/app/sql/Mimul.xml 정의
- DDL2iBatis.exe를 활용하여 자동 생성하게 하는 것이 개발 속도가 올라갑니다.
<?xml version='1.0'?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap namespace="Mimul">
<cacheModel id="mimul-cache" type="MEMORY">
<flushInterval hours="24"/>
<flushOnExecute statement="insertMimul"/>
<flushOnExecute statement="updateMimul"/>
<flushOnExecute statement="deleteMimul"/>
<property name="reference-type" value="WEAK" />
</cacheModel>
<resultMap class="com.mimul.dwr.model.Mimul" id="mimul-result" >
<result property="lseq" column="lseq" />
<result property="sseq" column="sseq" />
<result property="assetid" column="assetid" />
<result property="title" column="title" />
<result property="imgurl" column="imgurl" />
<result property="vodurl" column="vodurl" />
<result property="use" column="use" />
<result property="chgdate" column="chgdate" />
</resultMap>
<select id="getMimul" resultClass="com.mimul.dwr.model.Mimul"
parameterClass="Integer" >
<![CDATA[
SELECT lseq, sseq, assetid, title, imgurl, vodurl, use, chgdate
FROM mimul
WHERE lseq = #lseq#
]]>
</select>
<update id="updateMimul" parameterClass="com.mimul.dwr.model.Mimul" >
<![CDATA[
UPDATE mimul
SET sseq = #sseq#, assetid = #assetid#, title = #title#,
imgurl = #imgurl#, vodurl = #vodurl#, use = #use#,
chgdate = #chgdate#
WHERE lseq = #lseq#
]]>
</update>
<insert id="insertMimul" parameterClass="com.mimul.dwr.model.Mimul" >
<selectKey resultClass="int" keyProperty="lseq" >
SELECT mimul_lseq_seq.nextval as lseq FROM dual
</selectKey>
INSERT INTO mimul (lseq, sseq, assetid, title, imgurl, vodurl,
use, chgdate)
VALUES (#lseq#, #sseq#, #assetid#, #title#, #imgurl#, #vodurl#,
#use#, #chgdate#)
</insert>
<delete id="deleteMimul" parameterClass="com.mimul.dwr.model.Mimul" >
<![CDATA[
DELETE FROM mimul
WHERE lseq = #lseq#
]]>
</delete>
</sqlMap>
6. DAO에서 사용하기 위한 SqlConfig 객체 정의(dbcp+ibatis연결 정보 정의)
import java.io.File;
import java.io.Reader;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import com.jaeminara.common.log.LogPool;

public class SqlConfig {
private static SqlMapClient sqlMap = null;
private static SqlConfig instance_ = null;

private SqlConfig() throws Exception
{
Reader reader = null;
StringBuffer rsc = null;
try {
if (sqlMap == null) {
rsc = new StringBuffer(200);
rsc.append("com").append(File.separator);
rsc.append("jaeminara").append(File.separator);
rsc.append("dwr").append(File.separator);
rsc.append("app").append(File.separator);
rsc.append("sql").append(File.separator).append("sqlmap.xml");
reader = Resources.getResourceAsReader(rsc.toString());
sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
}
} catch (Exception e) {
System.out.println(e);
throw e;
} finally {
if (reader != null)
reader.close();
reader = null;
rsc = null;
}
}

public static SqlConfig instance()
{
try {
if (instance_ == null) {
synchronized (SqlConfig.class) {
if (instance_ == null)
instance_ = new SqlConfig();
}
}
} catch (Exception e) {
System.out.println(e);
}
return instance_;
}

/**
* Return SqlMapClient for SDP schema
*
* @return
*/
public static SqlMapClient getSqlMapInstance()
{
return sqlMap;
}
}

7. DAO 클래스 정의
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ibatis.sqlmap.client.SqlMapClient;
import com.mimul.common.log.LogPool;
import com.mimul.dwr.model.getMimulByLSEQ;

public class MimulDao
{
private SqlMapClient smc =
SqlConfig.instance().getSqlMapInstance();

public void MimulDao() {}

public Mimul getMimulByLSEQ(int lseq) throws Exception
{
Mimul mimul = null;

try {
if (lseq == 0) {
LogPool.instance("DAO").debug("getMimulByLSEQ() : Parameter is null!");
return null;
}
mimul = (Mimul) smc.queryForObject("getMimul", lseq);
} catch (Exception e) {
LogPool.instance("DAO").error(e);
throw e;
}
return mimul;
}
// 기타 필요한 함수 정의
}
크게 어려운 점은 없습니다. 그리고 transaction 무결성을 보장하기 위해서는 executor.startBatch();와 executor.executeBatch(); transaction 처리 로직을 넣으시면 됩니다.
나머진 자동으로 iBatis에서 트랜젝션 자원의 할당 및 해지의 라이프사이클을 관리해 줍니다.

 

'I.lib() > I.lib(iBatis)' 카테고리의 다른 글

Mybatis 설정  (0) 2013.04.01
Spring + iBatis 연동시 org.apache.commons.dbcp.BasicDataSource 설정  (1) 2010.04.27
Ibatis settings 요소  (1) 2010.04.12
[IBatis] 결과가 NULL 일때 대처방법.  (0) 2010.03.23
iBatis 강의 자료.  (0) 2010.02.24
.
:
Posted by .07274.
.. .. ..

[펌] : http://blog.naver.com/PostView.nhn?blogId=10353&logNo=10022595461&redirect=Dlog&widgetTypeCall=true

 

DBCP(DataBase Connection Pool)의 사용 - Java Application
<BASE target="_son">

DBCP(DataBase Connection Pool)의 사용 - Java Application
[01] DBCP(DataBase Connection Pool)의 사용
- 다운로드:
http://jakarta.apache.org
- DBCP: DBCP 구현 Liarary
http://jakarta.apache.org/site/downloads/downloads_commons-dbcp.cgi
commons-dbcp-1.2.1.jar
- Commons Collections: Collection Framework 구현
http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi
commons-collections-3.1.jar
- Commons Pool: 객체 버퍼링 라이브러리 구현
http://jakarta.apache.org/site/downloads/downloads_commons-pool.cgi
commons-pool-1.2.jar
1. DBCP구현
>>>>> DBCPConnectionMgr.java
package phonecalc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.pool.impl.GenericObjectPool;
public class DBCPConnectionMgr {
String jdbcDriver="oracle.jdbc.driver.OracleDriver";
String jdbcURL="jdbc:oracle:thin:@172.16.11.1:1521:ora10g2";
String user="ejb2030_04_1";
String password = "oracle";

/**
* 생성자
*
*/
public DBCPConnectionMgr() {
super();
try{
setupDriver(jdbcDriver, jdbcURL, user, password);
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
* Connection을 리턴함
* @return
*/
public Connection getConnection(){
Connection con = null;
try {
con = DriverManager.getConnection("jdbc:apache:commons:dbcp:ora10g2");
} catch(SQLException ex) {
ex.printStackTrace();
} finally {
}
return con;
}

/**
* Connection 설정
* @param jdbcDriver
* @param jdbcURL
* @param user
* @param password
* @throws Exception
*/
public void setupDriver(String jdbcDriver,
String jdbcURL,
String user,
String password) throws Exception {
// JDBC 드라이버 로딩
Class.forName(jdbcDriver);

// Connection Pool 생성
GenericObjectPool connectionPool = new GenericObjectPool(null);
connectionPool.setMaxActive(20); //최대 20개 접속 지원
connectionPool.setMaxIdle(5); //5개 대기중 설정

// 실제 DB와의 커넥션을 연결해주는 팩토리 생성
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
jdbcURL, // JDBC URL
user, // 사용자
password);

// Connection Pool이 PoolableConnection 객체를 생성할 때 사용할
// PoolableConnectionFactory 생성
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
connectionFactory,
connectionPool,
null, // statement pool
null, // 커넥션 테스트 쿼리: 커넥션이 유효한지 테스트할 때 사용되는 쿼리.
false, // read only 여부
true); // auto commit 여부

// Pooling을 위한 JDBC 드라이버 생성 및 등록
PoolingDriver driver = new PoolingDriver();

// JDBC 드라이버에 커넥션 풀 등록
driver.registerPool("ora10g2", connectionPool);
}

public void freeConnection(Connection con, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void freeConnection(Connection con, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(Connection con, PreparedStatement pstmt) {
try {
if (pstmt != null) pstmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void freeConnection(Connection con, Statement stmt) {
try {
if (stmt != null) stmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(Connection con) {
try {
if (con != null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(Statement stmt) {
try {
if (stmt != null) stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(PreparedStatement pstmt) {
try {
if (pstmt != null) pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void freeConnection(ResultSet rs) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

}

2. DBCP JAVA Test
>>>>> DBCP_Test.java
package test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import phonecalc.DBCPConnectionMgr;
public class DBCP_Test {

public static void main(String[] args) {
DBCPConnectionMgr dbcp = new DBCPConnectionMgr();
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql="SELECT count(*) as cnt FROM tab";

try{
con = dbcp.getConnection();
pstmt = con.prepareStatement(sql);

rs = pstmt.executeQuery();
if (rs.next()){
System.out.println(rs.getInt("cnt"));
}
}catch(Exception e){
System.out.println(e);
}finally{
dbcp.freeConnection(con, pstmt, rs);
}
}
}

 

 

추가

[펌] : http://godpage.tistory.com/236

 

GenericObjectPool의 설정에 관한 메모이다.
아래의 사이트에서 번역해 온 내용임을 밝힌다.
누군가 벌써 해놨을지도 모르지만..
http://commons.apache.org/pool/apidocs/org/apache/commons/pool/impl/GenericObjectPool.html

설정가능한 ObjectPool의 구현체


적절한 PoolableObjectFactory과 관련하여 GenericObjectPool은
임의의 오브젝트에게 견고한 풀링을 기능적으로 제공한다.

GenericObjectPool은 수많은 설정가능한 파라메터를 제공한다.

* maxActive : 주어진 시간에서의 풀(클라이언트가 보고있거나, 대기상태에 있는)에 의해 할당되어 관리되는오브젝트의 최대수. 설정이 양수가 아닌경우에는 한번에 풀에서 관리될 수 있는 오브젝트의 숫자에는 제한이 없다. 오브젝트의 수가 maxActive에 다다른 경우, 풀은 고갈되었다라고 말한다. 기본 설정은 8이다.

* maxIdle : 어떠한 시간에 풀에서 대기상태로 관리되어 질수 있는 최대 수이다. 음수인경우에는 풀에서 대기상태로 있는 오브젝트의 수에 제한이 없는것으로 간주한다. 기본설정은 8이다.

* whenExhaustedAction : borrowObject() 실행시 풀이 고갈되었을때의 행동을 지정한다.

* WHEN_EXHAUSTED_FAIL, borrowObject() 인경우 NoSuchElementExcption을 던질것이다.

* WHEN_EXHAUSTED_GROW, borrowObject() 인경우 새로운 오브젝트를 만들고 그것을 리턴한다.
(근본적으로 maxActive가 의미가 없어진다. )

* WHEN_EXHAUSTED_BLOCK, borrowObject() 인경우 새 오브젝트혹은 대기오브젝트가 사용가능해 질때까지 블럭- Object.wait()를 호출 - 시킨다. maxWait값이 양수인경우 수 밀리세컨드동안 borrowObject()를 블럭시키고 그후에
NoSuchElementException을 던지게 될것이다. maxWait가 음수라면 borrowObject()메서드는 영원히 블럭된다. (헐~)

* testOnBorrow가 설정되어 있으면, borrowObject()메서드를 통해 오브젝트를 리턴하기 전에 각각의 오브젝트의 유효성을 확인 하려고 할것이다.
(팩토리의 PoolableObjectFactory.validateObject(T) 메서드를 사용한다. )
유효성 체크에 실패하면 풀에서 그 오브젝트를 떨궈내고 다른 오브젝트를 빌려오게 될것이다. 기본설정은 false이다.

* testOnReturn이 설정되어 있으면, returnObject(T)를 통해 풀에 오브젝트를 반환하려고 할때
그 오브젝트의 유효성을 확인 하려고 할것이다. (팩토리의 PoolableObjectFactory.validateObject(T) 메서드를 사용한다.)
유효성 체크에 실패하면 풀어세 떨궈낸다. 기본 설정은 false이다.


옵션으로, 한가지 설정이 더있다. 풀에서 대기상태로 주저 앉아버린 오브젝트들을 쫓아낼 수 있는지 조사하기 위해 그리고 대기 오브젝트 수를 최소값이 되도록 보장하기위한 것이다. 이것은 한개의 'idle object eviction(대기 오부젝트 쫒아내기)' 스레드에
의해 수행되며, 비동기적으로 실행된다. 이 옵션을 설정할 경우는 주의해야한다. Eviction(오브젝트 쫓아내기)은 풀로 부터 오브젝트를 얻으려고하는 클라이언트 스레드와 다툴것이다. 그러므로 Eviction이 매우 빈번하게 일어난다면 성능상의 문제를 가져오게 될것이다. 대기 오브젝트를 쫓아내는 스레드는 아래의 어트리 뷰트들로 설정이 가능할것이다. (할것이다는 머임)

* timeBetweenEvictionRunsMillis : 대기 오브젝트를 쫓아내는 스레드가 "실행" 되기전에 얼마만큼 잠들어 있어야 되는지를 나타낸다. 음수로 되어있으면, 대기 오브젝트 쫓아내는 스레드는 올라오지 않는다. 디폴트 설정은 -1 이다. (즉 기본 설정은 사용안함이다.)

* minEvictableIdleTimeMills : 오브젝트가 풀에서 대기상태로 주저앉기 전에 idleTime에 의거해 쫓아 낼수 있는 최소 시간량을 명시한것이다. (시간제 피씨방, 만화방을 생각하면 되는건가.) 음수로 설정되어 있으면, idle time만으로는 어떤 오브젝트로 쫓아내지 않는다. 이 설정은 timeBetweenEvictionRunsMillis > 0 이 아니면 전혀 효과가 없다. 기본값은 30분이다.

* testWhileIdle : 대기 오브젝트이든 아니든 팩토리의 PoolableObjectFactory.validateObject(T)에 의해
유효성을 체크하게된다. 유효성체크에 실패한 오브젝트는 풀에서 떨궈진다. 이 설정은 timeBetweenEvictionRunsMillis > 0 이 아니면 전혀 효과가 없다.디폴트 설정은 false이다.

* softMinEvictableIdleTimeMills : 오브젝트가 풀에서 대기상태로 주저앉기 전에 대기오브젝트를 쫓아내는 스레드에 의해 쫓겨나게 되는 최소시간량을 몇시한다. 추가적인 조건으로 "minIdle"오브젝트의 인스턴스는 풀에 남아 있어야 된다. 음수로 설정되어 있으면 어떠한 오브젝트가 대기상태에 빠지더라도 쫓겨나지 않는다. timeBetweenEvictionRunsMillis > 0 이 아니면 이 설정은 무효다. 그리고 이 설정은 minEvictableIdleTimeMills가 유효한경우에는 무시된다. 디폴트 설정은 -1이다. (무효)

* numTestsPerEvictionRun : 대기 오브젝트를 쫓아내녀석(스레드)의 갯수. timeBetweenEvictionRunsMillis > 0 이 아니면 이 설정은 무효다. 기본값은 3이다.

대기 오브젝트를 존중해주려면, 풀은 LIFO큐로도 설정이 가능하다. 항상 가장 최근에 사용된 객체가 풀에서 부터 리턴된다. 또는 FIFO 큐는 풀의 가장 오래된 오브젝트 부터 빌려온다.


* lifo : 대기 오브젝트이든 아니든 풀은 last-in-first-out으로 객체를 리턴한다. 디폴트는 true

GenericObjectPoll은 PoolableObjectFactory가 없다면 쓸모없다. null이 아닌 팩토리는 풀이 생성되기 전에 생성자의 인자 혹은 setFactory(org.apache.commons.poll.PoolableObjectFactory)로 제공되어 져야한다.


.
:
Posted by .07274.
2013. 2. 1. 18:28

[펌] Threaded IO streams in Java I.lib()/I.lib(Java)2013. 2. 1. 18:28

.. .. ..

[펌] : http://lifeinide.blogspot.kr/2011/05/threaded-io-streams-in-java.html

Solving some interesting problems in the project I'm currently working on I started to analyse following problem: you have a large data set (eg. a file) on which you need to apply some expensive transformation (eg. compression). The key requirement is a performance.

The best performance demands can usually be effectively fulfilled using multi-threaded algorithms, especially on multiple CPU cores hardware. But you may not to have a multi-threaded transformation algorithm, or you can get a requirement to have many algorithms pluggable, not always designed for parallel work.

I started to think that it's easy to divide source data for chunks and start a thread pool to do the job. This is feasible when you can have multiple output streams, but if you want to write everything to a single file may be a little problematic. If you synchronize all threads to write to a single stream, you rather end up with multi-threaded algorithm working as well as simple single-threaded one. You will simply have no benefits from concurrency, when all threads need to wait for currently writing thread, having a lock. They simply don't do the transformation in the time they wait for the lock. In the result - only one thread works effectively.

If you want to have benefits from concurrency you need to design it in the way, that all threads can do their expensive work independently from the others, without waiting and synchronization.

The conception is following. We have N working threads, and each one is working its way and has been started in different time, in proportion to the others. If we had N buffers with previously established size for each thread, we could assert concurrent writing to these buffers from independent threads without any collision. If the buffer is overfilled, we can flush it to the output stream. If the lock is gained only for flushing, we potentially have other threads working in this time on their buffers, without waiting to have resource available. A Java implementation of this issue, conformed to input/output streams contract in Java is simple (I use apache commons to shorten time required to perform obvious things):

import java.io.BufferedOutputStream;
import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Wraps target output stream and allows to write to it from multiple threads, handling * underlying data. The data in target output stream is interleaved. It can be then * read then by ThreadedInputStream. * * The stream uses internal buffering and synchronization. The default buffer size is 512KB. * This means, than for each thread there'll be allocated 512KB buffer, when thread starts * to write into the stream. The buffer is flushed into the target output stream periodically. * * Maximum number of threads: Byte.MAX_VALUE * * The lifecycle is following: * <ol> * <li>Create new stream in a main thread * <li>For each worker thread: * <ul> * <li>use the stream in general way * <li>on the end of processing you need to call close(); the stream will not be really closed, * but will be flushed and thread buffers will be removed * </ul> * <li>Call close() in main thread to close stream permanently (as well as the target stream) * </ol> * * @author l0co@wp.pl */ public class ThreadedOutputStream extends OutputStream { protected DataOutputStream target; protected int bufSize = 512*1024; // default buffer size = 512 KB protected volatile byte threadsCount = 0; protected Thread creatorThread; /** Internal thread data holder and buffer **/ protected class ThreadStreamHolder { byte index = 0; int size = 0; byte[] buffer = new byte[bufSize]; public ThreadStreamHolder(byte index) { super(); this.index = index; } /** Flush data to the target stream **/ public void flush() throws IOException { if (size>0) { synchronized (target) { target.writeByte(index); // write thread index target.writeInt(size); // write block size target.write(buffer, 0, size); // write data size = 0; } } } public void write(int b) throws IOException { buffer[size++] = (byte) b; if (size>=bufSize) flush(); } } protected ThreadLocal<ThreadStreamHolder> threads = new ThreadLocal<ThreadedOutputStream.ThreadStreamHolder>(); /** * Creates stream using default buffer size (512 KB). * @param target Target output stream where data will be really written. */ public ThreadedOutputStream(OutputStream target) { super(); this.target = new DataOutputStream(target); creatorThread = Thread.currentThread(); } /** * Creates stream using custom buffer size value. * @param target Target output stream where data will be really written. * @param bufSize Buffer size in bytes. */ public ThreadedOutputStream(OutputStream target, int bufSize) { this(target); this.bufSize = bufSize; } @Override public void write(int b) throws IOException { ThreadStreamHolder sh = threads.get(); if (sh==null) { synchronized (this) { // to avoid more threads with the same threads count if (threadsCount==Byte.MAX_VALUE) throw new IOException("Cannot serve for more than Byte.MAX_VALUE threads"); sh = new ThreadStreamHolder(threadsCount++); // passing threadsCount and ++ is not atomic ! threads.set(sh); } } sh.write(b); } @Override public void flush() throws IOException { super.flush(); // flush the buffers on the end ThreadStreamHolder sh = threads.get(); if (sh!=null) sh.flush(); } @Override public void close() throws IOException { flush(); threads.remove(); // in main thread, close stream if (Thread.currentThread().equals(creatorThread)) target.close(); } public static final int TEST_THREADS = 64; // number of threads public static final double TEST_DPT_MAX = 1024*1024*10; // data amount per thread public static final int TEST_BLOCKSIZE = 1024*512; // default block size public static void main(String[] args) throws IOException { File f = new File("threados"); OutputStream target = new BufferedOutputStream(new FileOutputStream(f, false)); final ThreadedOutputStream out = new ThreadedOutputStream(target, TEST_BLOCKSIZE); ThreadGroup group = new ThreadGroup("threados"); // write some data by threads for (int i=0; i<TEST_THREADS; i++) { final int valueToWrite = (i+5)*20; new Thread(group, new Runnable() { @Override public void run() { try { int jMax = (int) (Math.random()*TEST_DPT_MAX) + 1; byte crc = 0; for (int j=0; j<jMax; j++) { out.write(valueToWrite+j); crc+=(valueToWrite+j); } out.write(crc); System.out.println("Some thread count: "+(jMax+1)); out.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } // wait for thread group to finish try { synchronized (group) { if (group.activeCount()>0) group.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } out.close(); } }
The static main() method shows an exemplary usage of the stream from multiple threads. You can manipulate constants to check how does it perform.

If we have such multi-threaded stream and the transformation algorithm performance depends on the input data, the buffers are filled in very random way and we have real benefits from multi-threading. I tested this and it performs very well.

But the output file made this way consists of random blocks of interleaved stream data, written to a single file. So how to handle with restoring data from source file (to apply reverse transformation)? This is why we put into the target stream the thread index and block size value before each block. We can now restore this sequentially, reading each thread source stream separately, or we can do this concurrently, using a thread pool with same threads count as in writing mode. All information can be easily retrieved from interleaved stream:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

/**
 * Allows to read data previously generated by ThreadedOutputStream. This class instance
 * works in single thread and retrieves data from interleaved stream, written by single 
 * thread. The raw source stream is divided on entries, depending on how many threads
 * were writing to the ThreadedOutputStream.
 * 
 * @author l0co@wp.pl
 */
public class ThreadedInputStream extends InputStream {
        
  protected DataInputStream source;
  protected byte entry;
        
  protected int bytesToRead = 0; // how many bytes can we read already from current data block?
        
  protected long pos = 0;
        
  /**
  * Creates the input stream.
  * 
  * @param source Newly instantiated, clean source input stream to raw data
  *      produced by ThreadedOutputStream.
  * @param entry Entry number. There can be many entries in source stream (0, 1, 2... etc,
  *      depending on count of writing threads).
  * @throws EOFException If there's no such entry yet (you need to manually close source then).
  */
  public ThreadedInputStream(InputStream source, byte entry) throws IOException {
    super();

    if (entry>Byte.MAX_VALUE)
      throw new IOException("Cannot serve for more than Byte.MAX_VALUE threads");
                
    this.source = new DataInputStream(source);
    this.entry = entry;
    lookupNextBlock();
  }
        
  protected void lookupNextBlock() throws IOException {
    while (true) {
      byte currentEntry = source.readByte();
                        
      if (currentEntry==entry) {
        // found next entry datablock
        bytesToRead = source.readInt();
        break;
      } else {
        // found next entry, but for different datablock (look for another)
        int blockSize = source.readInt();
        long toSkip = blockSize;
        while (toSkip>0) {
          long skip = source.skip(toSkip);
          if (skip<0)
            throw new EOFException("Cannot skip full datablock");
          toSkip -= skip;
        }                     
      }
    }
  }

  @Override
  public int read() throws IOException {
    if (bytesToRead<=0)
      try {
        lookupNextBlock();
      } catch (EOFException e) {
        return -1;
      }
                
    bytesToRead--;
    return source.read();
  }

  @Override
  public void close() throws IOException {
    source.close();
  }

  // test
  public static void main(String[] args) throws IOException {
    File f = new File("threados");
                
    ThreadGroup group = new ThreadGroup("threados");
                
    // read some data by threads
    Map<Byte, ByteArrayOutputStream> outmap = new LinkedHashMap<Byte, ByteArrayOutputStream>();
                
    try {
      byte i = 0;
      while (true) {
        InputStream source = new BufferedInputStream(new FileInputStream(f));
        try {
          final ThreadedInputStream is = new ThreadedInputStream(source, i++);
          final ByteArrayOutputStream out = new ByteArrayOutputStream();
          outmap.put(i, out);
                                        
          new Thread(group, new Runnable() {
            @Override
            public void run() {
              try {
                IOUtils.copy(is, out);
                is.close();
              } catch (IOException e) {
                e.printStackTrace();
              }
            }
          }).start();
        } catch (EOFException e) {
          source.close();
          break;
        }
      }
    } catch (EOFException e) {} // no more interleaved streams
                
    // wait for threads
    try {
      synchronized (group) {
        if (group.activeCount()>0)
        group.wait();
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
                
    for (byte b: outmap.keySet()) {
      byte[] ba = outmap.get(b).toByteArray();
      System.out.println(b+" ["+ba.length+"]: "+dumpByteArray(ba));
    }
  }
        
  public static String dumpByteArray(byte[] b) {
    StringBuffer sb = new StringBuffer();
    int i = 0;
    byte crc = 0;
    for (byte b1: b) {
      if (i++<20) {
        if (b1<10 && b1>=0)
          sb.append(0);
        sb.append((int) b1 & 0xFF);
        sb.append(',');
      }
                        
      if (i==b.length) {
        if (crc==b1)
          sb.append("crc ok");
        else
          sb.append("crc error");
      } else
          crc+=b1;
    }
    return sb.toString();
  }

}
Also this time main() method should get the file from output stream test, read it in thread pool, and perform simple checksum check.

Depending on requirements, ThreadedInputStream class can be used as a source for multi-threaded or sequential algorithm as well. You can start N threads to get N-stream to fetch data from it, or you can freely iterate through stream data entries in a single thread (in this case you need to perform N iteration).

I'm presenting this solution as a curiosity, but also as a real, working algorithm I used in the project. It works well for solving problems, when you want to parallelize sequential transformation algorithm (in my case it was compression) on a potentially large input data. Of course there's another question how to chunk source data, but this will depend on the source data itself. For unstructured file you can for example divide it to equal parts, and start N reading streams with different starting read index.
.
:
Posted by .07274.