IBM JAVA DUMP 설정 

.profile 에 설정하여 사용할수 있다.

 #### IBM JAVA dump ####
export IBM_HEAPDUMP=true
export IBM_HEAP_DUMP=true
export IBM_HEAPDUMP_OUTOFMEMORY=true
export IBM_JAVADUMP_OUTOFMEMORY=true
export IBM_HEAPDUMPDIR=/app_log/anylink/ibm_heap
export IBM_JAVACOREDIR=/app_log/anylink/ibm_core


jconsole을 이용한 로컬 PC에서 linux에 설치된 jeus 모니터링

 

1. "hostname -i"를 실행했을 때 결과로 "127.0.0.1"이 아닌 자기 IP가 나올 수 있게 설정해 준다. /etc/hosts 파일 수정.

127.0.0.1               hostname     localhost.localdomain localhost

아래와 같이 실제 IP와 hostname을 추가해준다.

127.0.0.1               localhost.localdomain localhost
xxx.xxx.xxx.xxx (localip)           hostname  <-- 추가

 

2. JMX 원격 접속을 할 수 있는 비밀번호를 설정한다. JRE_HOME/lib/management/jmxremote.password.template 파일을 복사해서 JRE_HOME/lib/management/jmxremote.password 파일을 생성하고 아래의 주석처리되어 있는 부분의 주석을 제거한다.


# monitorRole QED
# controlRole R&D

monitorRole  QED
controlRole   R&D


3. jeus세팅 $JEUS_HOME/config/`hostname`/JEUSMain.xml 의 <node>절의 <command-option>에

아래와 같이 추가해준다.($JEUS_HOME/bin/jeus 파일에 추가해도 됨.)

          <command-option>

               -Dcom.sun.management.jmxremote

            -Dcom.sun.management.jmxremote.port=port번호(ex 12345)

            -Dcom.sun.management.jmxremote.authenticate=false

            -Dcom.sun.management.jmxremote.ssl=false

         </command-option>           


4. 로컬 PC의 jconsole을 실행 하여 jeus가 실행되고 있는 remote 서버에 connect

(jconsole은 C:\Program Files\Java\jdk1.5.0_16\bin 에 위치 JDK1.5부터 지원)

cmd 창에서 jconsole 실행한다.

C:\>jconsole


HOST or IP : remote의 ip

Port : 3번에서 -Dcom.sun.management.jmxremote.port=port번호(ex 12345)로 설정해준 포트

User Name : 2번의 JRE_HOME/lib/management/jmxremote.password 파일의 monitorRole

Password 2번의 JRE_HOME/lib/management/jmxremote.password 파일의 QED

 

5. 모니터링 화면



* jconsole을 이용하여 로컬 PC에서 실행 되는 java application도 모니터링 가능하다.

java 실행시 -Dcom.sun.management.jmxremote 옵션으로 실행

java -Dcom.sun.management.jmxremote TCPServer


jconsole의 Local 탭의 해당 application의 PID를 선택한다.


아래와 같이 로컬PC에서 실행되는 application을 모니터링 할수 있다.

   

DHTML 속도 향상을 위한 몇 가지 팁

Mark Davis
Microsoft Corporation

요약: 이 기사는 성능에 주로 영향을 주는 일부 DHTML 기능에 대해 설명하고 사용자의 DHTML 페이지 기능을 향상시킬 수 있는 몇 가지 팁을 제시합니다(9페이지/인쇄 페이지 기준).

목차

소개
DHTML 변경 내용 일괄 처리
InnerText 사용
DOM을 사용한 개별 요소 추가
SELECT 요소에서 사용자 옵션 확장
DOM을 사용한 테이블 업데이트
한번 쓰기, 여러 번 사용
지나치게 동적인 속성은 피할 것
데이터 바인딩의 유용성
문서에 Expando 속성을 사용하지 말 것
클래스와 스타일 규칙을 전환하지 말 것
상위 요소를 찾기 전에 텍스트 범위를 축소
기타 자료

소개

Microsoft® Internet Explorer 4.0에 동적 HTML(DHTML)을 도입함으로써 웹 페이지 제작자 및 개발자에게 새로운 프로그래밍 모델 사용을 가능하게 했습니다. 이후 웹 페이지 제작자는 이 강력한 기능을 사용하여 웹 사용자에 대한 다양한 대화식 환경을 활성화함으로써 동적인 컨텐트, 스타일, 위치 지정 등을 제공하게 되었습니다. DHTML의 유연성은 종종 두 가지 이상의 방법을 제공하여 사용자가 원하는 작업을 수행하도록 합니다. 사용자의 요청을 처리하는 Internet Explorer의 HTML 구문 분석 및 렌더링 구성 요소를 이해하면 최상의 작업 방법을 결정할 때 많은 도움이 됩니다. 이 기사는 성능에 주로 영향을 주는 일부 DHTML 기능에 대해 설명하고 사용자의 DHTML 페이지 기능을 향상시킬 수 있는 몇 가지 팁을 제시합니다.

맨 위로

DHTML 변경 내용 일괄 처리

DHTML 웹 페이지 성능을 증가시키는 가장 중요한 방법은 HTML 컨텐트의 변경 내용을 적용하는 방법을 개선하는 것입니다. 웹 페이지를 업데이트하는 여러 가지 방법이 있다는 것을 알아 두어야 합니다. 클라이언트 스크립팅에서부터 웹 페이지 제작자는 여러 블록의 HTML 텍스트를 적용하거나 DHTML 개체 모델(영문 사이트) 또는 W3C 문서 개체 모델(DOM)(영문 사이트)을 사용하여 개별적인 HTML 요소에 액세스 할 수 있습니다. HTML 컨텐트에 변경 내용이 발생할 때마다 Internet Explorer의 HTML 구문 분석 및 렌더링 구성 요소는 해당 페이지의 내부 표시를 재구성하고 문서의 레이아웃 및 흐름을 다시 계산해서 변경된 내용을 표시해야 합니다. 실제 성능은 변경 내용과 웹 페이지 컨텐트에 따라 달라지지만 이러한 작업은 비용이 많이 들게 됩니다. 개별 요소를 액세스하는 대신 HTML 텍스트 블록을 적용하는 경우에는 HTML 파서가 호출되어야 하므로 추가 성능 비용을 초래하게 됩니다. HTML 텍스트를 허용하는 메서드와 속성에는 insertAdjacentHTML(영문 사이트) 및 pasteHTML(영문 사이트) 메서드와 innerHTML(영문 사이트) 및 outerHTML(영문 사이트) 속성이 포함됩니다.

팁 1: 하나의 스크립트 함수에서 HTML 컨텐트를 변경하십시오. 마우스 움직임에 대한 응답처럼 여러 이벤트 처리기를 사용하는 경우 한 위치에서 변경하십시오.

HTML 구문 분석 및 구성 요소 렌더링에 대한 중요한 또 다른 사실은 스크립트 이벤트 처리기 함수가 있는 경우나 setTimeout(영문 사이트)과 같은 메서드가 호출되는 경우와 같이 일단 어떤 스크립트가 제어권을 반환하면 레이아웃을 다시 계산하여 웹 페이지를 표시한다는 것입니다. 이제 Internet Explorer가 변경 내용을 처리하는 방법에 대해 알게 되었으므로 웹 페이지 성능 향상을 시작할 수 있습니다.

팁 2: 여러 업데이트를 수행하는 대신 HTML 문자열 하나를 작성하여 문서를 변경해 보십시오. HTML 컨텐트가 필요하지 않다면 innerText(영문 사이트) 속성 사용을 고려해 보십시오.

다음 예에서 느린 메서드는 innerHTML 속성을 설정할 때마다 HTML 파서를 호출합니다. 성능을 향상시키기 위해 문자열을 작성한 후에 innerHTML 속성이 할당되도록 할 수 있습니다.

실행 비교

느린 메서드:

  divUpdate.innerHTML = "";
 for ( var i=0; i<100; i++ )
 {
  divUpdate.innerHTML += "<SPAN>This is a slower method! </SPAN>";
 }

빠른 메서드:

  var str="";
 for ( var i=0; i<100; i++ )
 {
  str += "<SPAN>This is faster because it uses a string! </SPAN>";
 }
 divUpdate.innerHTML = str;

자세한 내용은 동적 컨텐트(영문 사이트)를 참조하십시오.

맨 위로

InnerText 사용

DHTML 개체 모델에서 HTML 요소의 텍스트 컨텐트는 innerText(영문 사이트) 속성을 통해 액세스되는 반면 W3C DOM은 별도의 하위 텍스트 노드를 제공합니다. DHTML innerText 속성을 직접 사용하면 DOM의 createTextNode(영문 사이트) 메서드를 호출하는 것 보다 더 빨리 요소 컨텐트를 업데이트할 수 있습니다.

팁 3: innerText 속성을 사용하여 텍스트 컨텐트를 업데이트하십시오.

다음 예는 innerText 속성을 사용하여 성능을 향상시키는 방법을 보여 줍니다.

실행 비교

느린 메서드:

  var node;
 for (var i=0; i<100; i++)
 {
  node = document.createElement( "SPAN" );
  node.appendChild(  document.createTextNode( " Using createTextNode() " ) );
  divUpdate.appendChild( node );
 }

빠른 메서드:

  var node;
 for (var i=0; i<100; i++)
 {
  node = document.createElement( "SPAN" );
  node.innerText = " Using innerText property ";
  divUpdate.appendChild( node );
 }

맨 위로

DOM을 사용한 개별 요소 추가

앞서 언급했던 것처럼 HTML 텍스트를 적용하여 액세스하는 방법은 HTML 파서를 호출하므로 이에 따른 성능 손실이 발생합니다. 이러한 이유 때문에 insertAdjacentHTML 메서드에 대한 단일 호출을 수행하는 대신 createElement(영문 사이트)와 insertAdjacentElement(영문 사이트) 메서드를 사용하여 요소를 추가하는 것이 더 빠릅니다.

팁 4: createElementinsertAdjacentElement 메서드를 호출하는 것이 insertAdjacentHTML 메서드를 호출하는 것보다 빠를 때가 있습니다.

DHTML 업데이트를 일괄 처리하고 insertAdjacentHTML 메서드에 대한 단일 호출을 수행하면 성능을 향상시킬 수는 있지만 DOM에서 직접 요소를 만드는 것이 더 효율적인 경우가 있을 수 있습니다. 그러므로 두 가지 방법을 모두 시도하여 어떤 것이 빠른지 결정하는 것이 좋습니다.

실행 비교

느린 메서드:

  for (var i=0; i<100; i++)
 {
  divUpdate.insertAdjacentHTML( "beforeEnd", "<SPAN> Uses insertAdjacentHTML() </SPAN>" );
 }

빠른 메서드:

  var node;
 for (var i=0; i<100; i++)
 {
  node = document.createElement( "SPAN" );
  node.innerText = " Uses insertAdjacentElement() ";
  divUpdate.insertAdjacentElement( "beforeEnd", node );
 }

맨 위로

SELECT 요소에서 사용자 옵션 확장

HTML 텍스트 메서드 사용에 관한 이전의 규칙에 대한 예외는 SELECT(영문 사이트) 요소에 많은 OPTION(영문 사이트) 요소를 추가할 때입니다. 여러 옵션에 액세스하려면 createElement 메서드를 호출하는 것보다 innerHTML 속성을 사용하는 것이 더 효율적입나다.

팁 5: SELECT 요소에 많은 옵션을 추가하려면 innerHTML을 사용하십시오.

문자열 연결을 사용하여 SELECT 요소 HTML 텍스트를 작성한 뒤 이 팁을 사용하여 innerHTML 속성을 설정합니다. 옵션이 많은 경우에는 문자열 연결도 성능에 영향을 미칠 수 있습니다. 이런 경우에는 배열을 만들고 Microsoft JScript® join(영문 사이트) 메서드를 호출하여 OPTION 요소 HTML 텍스트의 최종 연결을 수행합니다.

실행 비교

느린 메서드:

  var opt;
 divUpdate.innerHTML = "<SELECT ID='selUpdate'></SELECT>";
 for (var i=0; i<1000; i++)
 {
  opt = document.createElement( "OPTION" );
  selUpdate.options.add( opt );
  opt.innerText = "Item " + i;
 }

빠른 메서드:

  var str="<SELECT ID='selUpdate'>";
 for (var i=0; i<1000; i++)
 {
  str += "<OPTION>Item " + i + "</OPTION>";
 }
 str += "</SELECT>";
 divUpdate.innerHTML = str;

더 빠른 메서드:

 var arr = new Array(1000);
 for (var i=0; i<1000; i++)
 {
  arr[i] = "<OPTION>Item " + i + "</OPTION>";
 }
 divUpdate.innerHTML = "<SELECT ID='selUpdate'>" + arr.join() + "</SELECT>";
 

맨 위로

DOM을 사용한 테이블 업데이트

DHTML 테이블 개체 모델에 속해 있는 insertRow(영문 사이트) 및 insertCell(영문 사이트) 메서드를 사용하는 것 보다 DOM 메서드를 사용하여 테이블 행 및 셀을 삽입하는 것이 더 효율적입니다. 특히 크기가 큰 테이블을 만들 때 효율적입니다.

팁 6: 크기가 큰 테이블을 작성하려면 DOM 메서드를 사용하십시오.

실행 비교

느린 메서드:

  var row;
 var cell;
 for (var i=0; i<100; i++)
 {
  row = tblUpdate.insertRow();
  for (var j=0; j<10; j++)
  {
    cell = row.insertCell();
    cell.innerText = "Row " + i + ", Cell " + j;
  }
 }

빠른 메서드:

  var row;
 var cell;
 var tbody = tblUpdate.childNodes[0];
 tblUpdate.appendChild( tbody );
 for (var i=0; i<100; i++)
 {
  row = document.createElement( "TR" );
  tbody.appendChild( row );
  for (var j=0; j<10; j++)
  {
    cell = document.createElement( "TD" );
    row.appendChild( cell );
    cell.innerText = "Row " + i + ", Cell " + j;
  }
 }

맨 위로

한번 쓰기, 여러 번 사용

웹 사이트에서 공통적인 작업을 수행하는 데 스크립트를 사용한다면 둘 이상의 웹 페이지에서 재사용할 수 있도록 이러한 함수를 별도의 파일에 두는 것이 좋습니다. 스크립트 파일을 사용하면 코드 유지 관리를 향상시킬 뿐 아니라 스크립트 파일이 브라우저 캐시에 남아있게 되므로 웹 사이트를 방문하는 동안 사용자 컴퓨터에 한 번만 다운로드하면 됩니다. 개별 파일에도 일반 스타일 규칙을 적용하면 동일한 효과를 얻을 수 있습니다.

Tip 7: 공통 코드를 동작 또는 개별 파일에 두어 스크립트를 재활용하십시오.

스크립트 재활용 기능 향상을 위해 DHTML 첨부 또는 요소 동작(영문 사이트)에 공통 스크립트 작업을 배치하십시오. 동작은 스크립트를 재활용하고 HTML에서 액세스 가능한 구성 요소를 작성하여 사용자 고유의 개체, 메서드, 속성 및 이벤트를 사용한 DHTML 개체 모델 확장을 가능하게 합니다. 동작이 viewlink(영문 사이트) 기능을 사용하지 않는 경우 Internet Explorer 5.5 에서 사용 가능한 lightweight(영문 사이트) 동작 기능을 사용하면 코드 간략화를 더욱 효과적으로 수행할 수 있습니다. 또한 스크립트 코드가 단일 SCRIPT(영문 사이트) 블록 내에 있는 경우 성능을 더욱 향상시킬 수 있습니다.

지나치게 동적인 속성은 피할 것

동적 속성(영문 사이트)을 통하여 웹 페이지 제작자는 속성 값으로 식을 사용할 수 있습니다. 이 식은 실행 시간에 계산되어 속성에 그 결과 값을 적용합니다. 이 기능은 강력합니다. 페이지 스크립트 양을 감소시킬 수 있지만 식이 주기적으로 다시 계산되고 종종 다른 속성 값에 따라 변하기 때문에 성능에 좋지 않은 영향을 줄 수도 있습니다. 위치 지정 속성인 경우에는 특히 그러합니다.

팁 8: 동적 속성 사용을 자제하십시오.

맨 위로

데이터 바인딩의 유용성

데이터 바인딩(영문 사이트)은 웹 페이지의 HTML 요소에 데이터베이스 쿼리 결과 값 또는 XML 데이터 섬(영문 사이트) 컨텐트를 바인딩할 수 있는 강력한 기능입니다. 또한 데이터를 가져오기 위해 서버로 돌아가지 않고도 데이터 정렬 및 필터링 기능 및 다른 데이터 뷰를 제공할 수 있습니다. 회사의 데이터를 선 그래프, 막대 그래프 또는 파이 차트로 나타내고 지점, 제품 또는 판매 기간별로 데이터를 정렬하는 단추가 있는 웹 페이지를 상상해 보십시오. 한 번만 서버를 방문하면 이 모든 기능을 제공할 수 있습니다.

팁 9: 다양한 클라이언트쪽 데이터 뷰를 제공하려면 데이터 바인딩을 사용하십시오.

데이터 바인딩에 대한 자세한 정보는 다음 기사를 참조하십시오.

맨 위로

문서에 Expando 속성을 사용하지 말 것

expando(영문 사이트) 속성은 모든 개체에 추가할 수 있는 임의 속성입니다. 이 속성은 현재 웹 페이지에 정보를 저장하는 경우 유용하며 DHTML 개체 모델을 확장하는 또 다른 방법을 제공합니다. 예를 들어 HTML 요소에 clicked 속성을 할당하여 어느 요소가 클릭되었는지를 알려주는 데 사용할 수 있습니다. 이벤트를 발생시켜 이벤트 처리기 함수에 추가적인 컨텍스트 정보를 제공하는 경우에도 expando 속성을 사용할 수 있습니다. expando 속성을 어떻게 사용하건 document(영문 사이트) 개체에는 이 속성을 설정하지 마십시오. 속성을 설정하면 속성을 액세스할 때 문서가 추가적인 재계산을 수행해야 합니다.

팁10: window(영문 사이트) 개체에 expando 속성을 설정하십시오.

실행 비교

느린 메서드:

for (var i=0; i<1000; i++)
 {
  var tmp;
  window.document.myProperty = "Item "+i;
  tmp = window.document.myProperty;
 }

빠른 메서드:

for (var i=0; i<1000; i++)
 {
  var tmp;
  window.myProperty = "Item "+i;
  tmp = window.myProperty;
 }

맨 위로

클래스와 스타일 규칙을 전환하지 말 것

클래스와 스타일 규칙을 전환하면 재계산과 전체 문서 레이아웃이 필요하기 때문에 부담이 큰 작업이 될 수 있습니다. 웹 사이트의 컨텐트를 다른 방법으로 보기 위해 스타일시트를 사용하는 경우, 요소의 className(영문 사이트) 속성이나 styleSheet(영문 사이트) 개체를 수정하는 대신 변경할 요소의 style(영문 사이트) 개체를 직접 수정하는 것이 좋습니다.

팁 11: 컨텐트 표시를 변경할 때 style 개체를 직접 수정하십시오.

상위 요소를 찾기 전에 텍스트 범위를 축소

TextRange(영문 사이트) 개체는 사용자가 선택하거나 BODY(영문 사이트)와 같은 HTML 요소에서 가져온 텍스트의 영역입니다. 텍스트 범위의 상위는 parentElement(영문 사이트) 메서드를 호출해 확인합니다. 복잡한 문서 범위는 parentElement 메서드를 호출하기 전에 collapse(영문 사이트) 메서드를 호출하는 것이 효율적입니다.

팁 12: parentElement 메서드를 액세스하기 전에 텍스트 범위를 축소하십시오.

자세한 내용은 TextRange 개체 사용(영문 사이트)을 참조하십시오.

맨 위로

기타 자료

성능에 대한 다른 정보를 보려면 다음 기사를 참조하십시오.

Mark Davis는 Internet Explorer SDK 설명서 팀의 소프트웨어 디자인 엔지니어입니다. 그는 Internet Explorer 연구에 빠져 있거나 시간이 나면 미국 서북부 지역을 하이킹하는 것이 취미입니다.

 

Tistory 태그:

Java's Runtime.exec() and External Applications
by Mitchell S. Baltuch ( mitch@unidata.ucar.edu)

Java was developed with an eye toward platform independence, freeing the software developer from the unpleasant and tedious task of cross-platform porting and testing of software. All too often, however, developers are not able to write completely new applications, but have to interface to existing legacy software and operating systems. This poses a problem as Java cannot make use of many utilities and system-dependent features that these programs have relied upon in the past.

There are two solutions to this problem, both of which sacrifice platform independence. The first is to make use of native methods in your Java code. You lose the portability, but achieve the result. Native methods can also be more difficult to implement, which can make them less desirable to use.

The second option is to satisfy the programming needs by executing external utilities and applications from within a Java program. In Java, this means employing the Runtime class exec() method. Like the fork(3) C function, Runtime.exec() allows you to execute a program; unlike fork(), it does not allow you to directly control the environment. Runtime.exec() provides a simple interface to platform-dependent utilities and applications, although at the expense of platform independence. In an environment where Java applications must coexist with other non-Java applications this will often be a valid trade-off.

In its simplest form, exec() is quite easy to use:

	Process p = Runtime.getRuntime().exec("/bin/ls");

The problem with this form is that it gets you nowhere. When Java forks a new process it redirects STDIN, STDOUT and STDERR. Therefore the results of "/bin/ls" do not go to the screen. Instead you must use the getInputStream(), getOutputStream() and getErrorStream() methods of the Process object to communicate with the program that you executed. Listing 1 shows an example using the Unix ls(1) command.

				  Listing 1
import	java.io.*;

class execInput {
    public static void main(String Argv[]) {
	try {
	    String ls_str;

	    Process ls_proc = Runtime.getRuntime().exec("/bin/ls -aFl");

	    // get its output (your input) stream

	    DataInputStream ls_in = new DataInputStream(
                                          ls_proc.getInputStream());

	    try {
		while ((ls_str = ls_in.readLine()) != null) {
		    System.out.println(ls_str);
		}
	    } catch (IOException e) {
		System.exit(0);
	    }
	} catch (IOException e1) {
	    System.err.println(e1);
	    System.exit(1);
	}

	System.exit(0);
    }
}

Some points to be aware of with this code. In the JDK1.0.2 implementation, you must supply a complete path to whatever it is that you are executing. There is no facility in that version of the JDK to get the shell environment that you are running in, hence no PATH variable is known. This is fixed in JDK1.1, however it is always good practice to fully qualify the path.

Shell built-in commands will not work here. A prime example of this is the DOS "dir" command. This is a built-in and will not execute. You would need to use "command \c dir" as the command string.

If you pass the executable command as a single String, the exec() method will break the string into multiple strings, breaking the original string on white space. This will cause some trouble when trying to do things like I/O redirection.

Consider the following line of code:

	Process p = Runtime.getRuntime().exec("/bin/sh -c /bin/ls > ls.out");

This is intended to execute a Bourne shell and have the shell execute the ls command, redirecting the output of ls to the file ls.out. The reason for using /bin/sh is to get around the problem of having stdout redirected by the Java internals. Unfortunately, if you try this nothing will happen. When this command string is passed to the exec() method it will be broken into an array of Strings with the elements being "/bin/sh", "-c", "/bin/ls", ">", and "ls.out". This will fail, as sh expects only a single argument to the "-c" switch. To make this work try:

	String[] cmd = {"/bin/sh", "-c", "/bin/ls > out.dat"};
	Process p = Runtime.getRuntime().exec(cmd);

Since the command line is already a series of Strings, the strings will simply be loaded into the command array by the exec() method and passed to the new process as is. Thus the shell will see a "-c" and the command "/bin/ls > ls.out" and execute correctly.

Using Runtime.exec() can make integration with system utilities, tools and legacy software achievable, although, as already noted, at the expense platform independence. If you must work with existing software and tools, it is often the easiest, and most straight-forward approach to the problem. But beware of the gotchas. They can often lead to unexpected results.

 

번 호
1

ERROR
Exception in thread "main" java.lang.NoClassDefFoundError: 파일명

발생되는 경우
클래스 파일을 찾을 수 없는 경우

조 언
실행하려는 클래스 파일 이름이 제대로 되어 있는지 확인한다.
또한, CLASSPATH 설정이 제대로 되어 있는지 확인하며 (도스모드에서 set명령어)
만약, 되어있지 않다면 설정한다. (CLASSPATH = jdk1.3/jre/lib/rt.jar; 2-1강좌 참조)

번 호
2

ERROR
cannot resolve symbol
symbol : class in(에러가 난 부분)
location : class StackTest(찾으려는 위치)

발생되는 경우
이해할 수 없는 클래스나 메소드, 변수명이 올경우

조 언
보통 이 에러는 철자가 틀렸을 경우에 많이 발생한다. 클래스, 메소드, 변수의 철자를 세심히 확인해 본다.
특히, 철자를 확인할때 대소문자 구분을 확실히 체크한다.(자바는 대소문자를 구별한다.) 그리고 클래스에서 발생할 경우 import를 해주었는지 확인해 봅니다.

번 호
3

ERROR
non-static variable 변수이름(or method 메소드이름) cannot br referenced from a static context

발생되는 경우
static 메소드 안에서 static 으로 선언되지 않은 메소드나 변수를 참조(사용)했을 경우.
특히, 메소드의 경우는 인스턴스를 사용하지 않고 static메소드 내에서 바로 선언한 경우.

조 언
static 선언자의 사용여부를 살펴보고 static 메소드 안에 static으로 선언되어지지 않은 메소드나 변수가 있느지 확인해본다.
만약 그런것이 있으면 메소드를 새로 만들어 그쪽에서 선언한다. 단, 인스턴스를 생성해서 불러줘야 한다는 것을 잊지 말아야한다.

번 호
4

ERROR
valiable 변수명 might not have been initialized

발생되는 경우
지역변수인 변수명의 변수가 초기화가 되어있지 않았을 경우

조 언
지역변수(메소드 내에서 선언한 변수)를 초기화 하지 않은채 선언했을 경우 발생한다.
멤버 필드가 아닌 경우는 반드시 변수 선언시 초기화를 해주어야 한다.
(멤버 필드는 자바 프로그램 자체에서 자동으로 default값으로 초기화 해준다.)

번 호
5

ERROR
class 클래스명 is public, should be declared in a file named 클래스명.java

발생되는 경우
클래스명이 public으로 선언되었는데 파일명과 다를 경우

조 언
public으로 선언된 클래스가 있다면 반드시 그 클래스명과 파일명이 같아야 한다.
클래스명과 파일명의 대소문자 및 철자가 같은지 비교해 본다.
또한, public으로 선언된 클래스가 하나 이상 있는지 찾아본다.(반드시 하나만 있어야한다.)

번 호
6

ERROR
push(java.lang.object)[메소드(인자로 받을 수 있는 형)] in java.util.Stack(메소드의 클래스) cannot be applied to (int)[잘못 들어간 형]

발생되는 경우
메소드에서 인자를 받을 때 받을 수 있는 형이 아닌 자료형 또는 클래스형을 사용할 경우

조 언
사용하는 메소드의 API를 참고하여 어떤 형을 인자로 받을 수 있는지 찾아본다.
API를 보지 못할 경우는 각 자료형으로 인자를 직접 바꾸어 본다.

번 호
7

ERROR
java.lang.NoSuchMethodError: main
Exception in thread "main"

발생되는 경우
클래스 파일 안에서 main() 메소드를 찾을 수 없는 경우

조 언
자바 애플릿이 아닌 이상 자바 애플리케이션은 반드시 main() 메소드를 사용해야 합니다.
main() 메소드를 빼먹지 않았는지 확인해 보십시요. 또한 public static void main(String[] args) 형식으로 씌어졌는지도 확인해 보십시요.
(main 클래스는 반드시 위와 같은 형식으로 만들어져야 합니다.)

번 호
8

ERROR
unreported exception java.io.IOException(Exception명); must be caught or declared to be thrown

발생되는 경우
예외가 발생하는데 예외처리를 해주지 않았을 경우

조 언
예외를 발생하는 메소드 같은 경우는 반드시 예외처리를 해주어야 합니다.
예외를 발생하거나 예외처리를 해야하는 메소드는 API를 확인해 보시면 알 수 있습니다.
그렇지 않다면 컴파일 후 지금처럼 에러가 난 예외를 예외처리해 주시면 됩니다.
또한, 예외를 처리할 때는 메소드 차원에서 throws 예외명을 이용해서 처리할 수 있고
try{} catch{} 구문을 이용해서 직접 처리해 주셔도 됩니다.(예외 강좌를 참고하세요.)
특히, 예외도 클래스이므로 반드시 예외가 들어간 패키지를 import 해주어야 합니다.

번 호
9

ERROR
Note : Calculator.java(파일명) uses or overrides a deprecated API.
Note : Recompile with -deprecation for details.

발생되는 경우
JDK 버전이 높아졌거나 보안등의 기타이유로 사용이 deprecated된 메소드를 사용한 경우

조 언
이건 예외라기 보다는 경고 입니다.(실행하면 됩니다.^^)
JDK가 버전이 높아지거나 보안등의 이유에 따라 예전에 만들어졌지만 필요가 없어지거나 대체된 메소드가 생겨났습니다.
그런 메소드를 deprecated 되었다고 하는데 이것은 API상에 나왔있습니다.
또한, 컴파일할때 -deprecation 옵션주면 어떤 메소드가 deprecate됐는지 알수있읍니다.
사용이 중지 됐다고 보기 보다는 사용을 가능하면 하지 않게끔 해주는 거죠.
대치되었거나 버전 업된 메소드를 사용하시면 됩니다.

번 호
10

ERROR
MouseEvent(클래스명) should be declared abstract; it does not define mouseDragged(java.awt.event.MouseEvent)[메소드명(메소드가 포함된 클래스)] in MouseEvent(클래스명)

발생되는 경우
implements한 Interface의 모든 메소드를 구현하지 않아서 발생됨

조 언
Interface는 모든 메소드가 선언만 되고 구현되지 않은 추상(abstract) 메소드입니다.
만약 Interface를 implements하려면 implements한 클래스가 Interface에서 선언한 모든 메소드를 구현해 주어야 합니다.
하나라도 빠질 경우 implements한 클래스도 추상 클래스로 보고 에러가 발생합니다.
에러에 구현해 주어야할 메소드명이 나오므로 그곳에 쓰여있는 메소드를 구현해 주면 됩니다.
만약, 그 메소드를 구현해 주었는데 에러가 나면 철자 및 대소문자를 다시 확인해 보십시요.

번 호
11

ERROR
incompatible types
found : /null(입력한 자료형)
required : int(요구하는 자료형)

발생되는 경우
입력을 했을때 맞지 않는 자료형이나 클래스형을 입력할 경우

조 언
incompatible 은 '성미가 맞지 않는','모순된' 이라는 뜻을 가진 단어 입니다.
단어뜻 처럼 입력 경우 required 에 나타난 자료형 및 클래스형을 요구하는데
found 에서 나타난 자료형 및 클래스형을 써주어서 입력을 하지 못하게 되어서 발생하는 에러입니다.
found 에 나타난 자료형을 required 에 나타난 자료형으로 변경해 주시면 됩니다.

번 호
12

ERROR
package java.servlet(패키지명) does not exist

발생되는 경우
import한 패키지가 존재하지 않을 경우

조 언
import한 패키지가 존재하지 않을 경우에 발생하는 에러입니다.
철자와 대소문자를 먼저 확인하고 CLASSPATH 설정을 확인해 보시기 바랍니다.
또한 그 곳에 패키지가 jar파일로 있는지도 확인해 보셔야 합니다.
(API에 나와있는 패키지는 rt.jar에 다 있습니다.
단 javax가 붙거나 다름으로 시작되는 확장 패키지는 설치해 주어야합니다.(javax.swing 제외))

번 호
13

ERROR
java.lang.NullPointerException
Exception in thread "main"(메소드) java.lang.NullPointerException
at java.awt.Container.addImpl(Container.java:341)... [에러가 일어난 부분]

발생되는 경우
참조하거나 사용한 클래스 또는 자료형이 초기화 되지 않은 경우

조 언
보통 이것은 awt나 배열 부분에서 자주 발생하는데 초기화를 해주지 않아서 많이 발생합니다.
자바의 변수들은 기본적으로(자동으로 초기화 되는 멤버필드등을 제외하고) 초기화를 요구합니다.
에러에 체크된 부분을 검토해 보시고 초기화를 해주십시요.

번 호
14

ERROR
';'(빠진 부분) expected

발생되는 경우
문법상으로 써야할 것을 쓰지 않은 경우 발생합니다.

조 언
주로 ';'을 안써주시거나 아님 '()'(괄호)를 열기만 하고 닫지 않은 실수를 할 경우 발생합니다.
대부분 이 에러가 발생한 경우는 에러에 나온것을 소스에 추가해 주시면 됩니다.

번 호
15

ERROR
unexpected type
required : value(요구하는 타입)
found : class(소스상 써준 타입)

발생되는 경우
써주어야 할 타입이 아닌 잘못된 타입을 써주었을 경우

조 언
unexpected type 에러를 해석하면 '기대하지 않은 타입'이란 뜻을 가지고 있습니다.
즉, 원하는 타입(required)이 아닌데 잘못된 타입(found)을 써준 경우 발생합니다.
에러 체크된 부분의 타입을 required 에서 나타난 타입으로 변경해 주시면 됩니다.

번 호
16

ERROR
java.lang.ArrayIndexOutOfBoundsException
at Test.main(Test.java:10)[클래스.메소드(파일명:에러난 위치)]
Exception in thread "main"(예외가 던져진 메소드)

발생되는 경우
배열의 범위를 넘어선 값을 넣었을 경우

조 언
위의 에러는 특이하게 컴파일은 이상없이 되지만 실행을 하면 발생하는 에러입니다.
배열의 범위를 넘어설 경우에 발생하므로 에러난 위치의 배열의 참조 범위를 확인해보시고
선언해둔 배열의 범위에 맞게 조정해 주시면 됩니다.

번 호
17

ERROR
illegal start of expression

발생되는 경우
선언자(modifier)를 잘못 집어 넣은 경우

조 언
에러의 단어뜻을 확인해 보면 '표현의 시작이 부적격 합니다.'하고 해석할 수 있습니다.
보통 선언자가 맞지 않거나 쓰일데가 아닌데 선언자를 줄 경우에 많이 발생합니다.
특히 메소드안에서 static 선언자를 쓴 경우에는 직격으로 볼수 있죠.
에러가 난 부분의 선언자를 제거하거나 맞는 것인지 다시 확인해 보십시요.

번 호
18

ERROR
java.io.InputStream(클래스) is abstract; cannot be instantiated

발생되는 경우
abstract로 선언된 클래스를 직접 new 명령어를 이용하여 인스턴스화 할 경우

조 언
abstract로 선언된 클래스를 직접 new 명령어를 이용하여 인스턴스화 할 경우에 발생하는 에러입니다.
왜냐하면, 추상 클래스는 직접 new 명령어를 이용하여 인스턴스화 할수 없기 때문입니다.(객체를 못만든다구요.)
이 경우에는 인스턴스를 다른 방법으로 생성하시면 됩니다.
예를 들어 인스턴스를 반환하는 메소드를 이용한다거나 상속을 통해서 상속받은 클래스의 인스턴스를 만들어
직접 인스턴스를 만드는 효과를 낼수도 있구요. 원하시는 방법으로 바꾸어 보시길...

번 호
19

ERROR
local variable name(변수명) is accessed from within inner class; needs to be declared final

발생되는 경우
Local Class의 변수를 final로 선언하지 않은 경우

조 언
Local Class의 변수는 참조변수의 참조값 변동을 방지하기 위하여 final 선언자를 붙여주어야 합니다.
변수에 final 선언자를 붙이면 변수는 값을 변동할 수 없는 상수 처럼 쓰이며
만약 이 값을 참조할 경우 자바는 이 값을 넘기는게 아니라 이 값의 복사본을 참조 값으로 넘기게 됩니다.
그러므로 Local Class에서 참조값 변동없이 변수를 참조할 수 있게 되는 것입니다.
Local Class를 정의해준 곳을 살펴보고 final 선언자를 확실하게 확인하시기 바랍니다.

번 호
20

ERROR
inner classes cannot have static declarations

발생되는 경우
내부 클래스 안에서 static 선언자를 쓴 경우

조 언
내부 클래스 안에서는 static 선언자를 쓸수 없습니다.
내부 클래스 안에서 사용된 static 선언자를 제거해 주십시요.

번 호
21

ERROR
referenceto List is ambiguous,both class java.util.List(클래스) in java.util(패키지)
and class java.awt.List(클래스) in java.awt(패키지) match

발생되는 경우
클래스 사용시 다른 패키지내에 동일이름의 클래스들이 있어서 참조가 모호할 경우

조 언
예시를 보면 아시겠지만 import 한 패키지중에 같은 이름을 사용하는 클래스를 클래스 이름만으로
생성함으로서 참조가 모호해질 경우 발생하는 에러입니다.
이와 같은 경우는 import를 하나 제거 하거나 아님 java.util.List 이런식으로
직접 그 클래스의 패키지를 같이 써줌으로서 모호성을 제거할수 있습니다.

번 호
22

ERROR
m()(메소드명) in B(클래스명) cannot override m()(메소드명) in A(클래스명);
attempting to use incompatiable return type

발생되는 경우
클래스를 상속받고서 메소드를 오버라이드 하고자할때 잘못한 경우

조 언
클래스를 상속받고서 메소드를 오버라이드 할 경우에는 지켜야 하는 규칙이 있습니다.
1. 메소드의 이름이 같아야 합니다.
2. 메소드의 파라미터 개수, 데이터형이 같아야 합니다.
3. 메소드의 리턴형이 같아야 합니다.
4. 상위 메소드와 동일하거나 더 구체적인 Exception을 발생시켜야 합니다.
5. 상위 메소드와 동일하거나 접근범위가 더 넣은 접근 제한자를 사용해야 합니다.
님의 메소드 오버라이드시 위 규칙을 잘 지켰는가를 다시 한번 확인해 보세요.

번 호
23

ERROR
getPathBetweenRows(int,int)(메소드) has protected access in javax.swing.jTree(클래스)

발생되는 경우
protected로 선언된 메소드를 상속 없이 직접 불러쓸 경우

조 언
protected로 선언되어 있는 메소드는 상속하거나 같은 package에 있을 때만 쓸 수 있습니다.
상속해서 다시 public 메소드로 값을 받던지 아니면
public 메소드 중에서 비슷한 기능을 하는 메소드가 있는지 찾아서 바꾸어주어야 합니다.

번 호
24

ERROR
invalid method declaration; return type required

발생되는 경우
리턴 타입을 쓰지 않아 메소드의 선언이 잘못된 경우

조 언
리턴 타입을 쓰지 않아 메소드의 선언이 잘못된 경우에 발생하는 에러이므로
에러가 발생한 메소드를 확인해보고 리턴 타입을 맞게 적어주어야 합니다.

번 호
25

ERROR
Error occurred during initialization of VM
java.lang.ExceptionInInitializerError

발생되는 경우
static으로 선언된 변수중 초기화가 안되어 있는 것이 있는 경우

조 언
static으로 선언된 변수중에 초기화가 안된게 있는 경우에 발생하는 에러이므로
에러가 발생한 변수를 확인해보고 알맞은 초기화를 시켜주거나 변수의 위치를
자동 초기화가 가능한 메소드 밖의 클래스 변수로서 사용하게 합니다.

번 호
26

ERROR
Error opening registry key 'Software\JavaSoft\Java Runtime Environment'
Error: could not find java.dll
Error: could not find Java 2 Runtime Environment

발생되는 경우
중복설치 등으로 인해 레지스트리 키값이 잘못되어 있는 경우

조 언
중복설치 등으로 인해 레지스트리 키값이 잘못되어 있는 경우에 발생하는 에러이므로
레지스트리 편집기를 열어서 HKEY_LOCAL_MACHINE -> SOFTWARE -> JavaSoft에
보시면 3개의 키가 있을 겁니다. 그중에서 첫번째 키인 Java 런타임 환경 을 마우스
오른쪽 버튼으로 클릭하여 Java Runtime Environment로 이름을 바꿔주시면 됩니다.

번 호
27

ERROR
Error Registry Key 'Sofrware\JavaSofrware\Java Runtime Environment\CurrentVerison'
has value '1.1',but '1.3' is requried.
Error: could not find java.dll
Error: could not find java 2 Runtime Enviroment.

발생되는 경우
중복설치 등으로 인해 레지스트리 키값의 자바 버전이 잘못되어 있는 경우

조 언
중복설치 등으로 인해 레지스트리 키값의 자바 버전이 잘못되어 있는 경우에 발생하는 에러이므로
레지스트리 편집기를 열어서 HKEY_LOCAL_MACHINE -> SOFTWARE -> JavaSoft -> Java Runtime Environment의 Current version의 값을 1.3으로 되어있는지 확인해 주시면 됩니다.

번 호
28

ERROR
java.lang.ClassNotFoundException: org.gjt.mm.mysql.Driver(드라이버명)

발생되는 경우
JDBC로 데이터 베이스에 연결하는 중 드라이버를 찾지 못할 경우

조 언
JDBC를 연결하는 중에 드라이버를 찾지 못할 경우에 발생하는 에러이므로
각 데이터 베이스에 맞는 드라이버가 제대로 다운로드 되었는지 확인해 보시고
드라이버의 위치가 클래스 패스에 잡혀 있는지 확인해주시면 됩니다.

번 호
29

ERROR
Method printIn(java.lang.String)(메소드명) not found in class java.io.PrintStream(클래스명)

발생되는 경우
자신이 사용한 클래스의 메소드가 맞지 않는(=없는)경우

조 언
자신이 사용한 클래스의 메소드가 맞지 않는(=없는) 경우에 발생하는 에러이므로
API를 통해서 사용하고자 하는 클래스와 메소드를 다시 한번 확인해 봅니다.
보통 이경우 메소드의 철자나 대소문자를 잘못 쓴 경우가 많으니 그점을 유심히 살표봅니다.
마지막으로 철자와 대소문자도 맞는다면 메소드의 인자의 객체형을 맞게 주었는지 확인해보면 됩니다.

자바를 하다보면 에러와 만나게 되는데요... 자바는 친절해서 몇번라인에 에러가 났으니 고쳐달라고 콘솔창에 표시를 합니다... 그래도 모르는 에러들이 있는데요.. 조금이라도 참고해 보아요

 

Tistory 태그: ,

1. Collection

  • Map은 key와 value를 가진 집합이며, 중복을 허용하지 않는다.
  • 즉, 한개의 key에 한개의 value가 매칭된다.
  • java.util 패키지에 여러 집합들을 사용하기 위한 여러 interface와 class 들이 정의되어 있다.

2. HashMap

  • HashMap은 Map interface를 implements 한 클래스로서 중복을 허용하지 않는다.
  • Map의 특징인 key와 value의 쌍으로 이루어지며, key 또는 value 값으로써 null을 허용한다.
  • 아래의 예는 HashMap을 사용한 간단한 예제이다.
    public class HashMapTest
    {
        public static void main(String argv[])
        {
            HashMap hm = new HashMap();
            System.out.println(hm.put("aaa", "111"));
            System.out.println(hm.put("bbb", "222"));
            System.out.println(hm.put("aaa", "444"));
            System.out.println(hm.put("ccc", "333"));    
            System.out.println(hm.put("ccc", null));       
            
            System.out.println("HashMap size : " + hm.size());
            
            Set set = hm.keySet();
            Object []hmKeys = set.toArray();
            for(int i = 0; i < hmKeys.length; i++)
            {
                String key = (String)hmKeys[i];   
                System.out.print(key);
                System.out.print(" - ");
                System.out.println((String)hm.get(key));
            }
        }
    }
    /**
    실행:java HashMapTest
    결과:
    null
    null
    111
    null
    333
    HashMap size : 3
    ccc - null
    bbb - 222
    aaa - 444
    */   

3. TreeMap

  • TreeMap역시 중복을 허용하지 않으며, key와 value의 쌍으로 이루어져 있다.
  • HashMap과 다른 점은 SortedMap을 implements 하였으므로, key 값들에 대한 정렬이 이루어진다는 점이다.
  • 아래의 예는 TreeMap을 사용하여 각 요소가 몇몇 이나 나왔는지 알아보는 간단한 예제이다.
    import java.util.*;
    
    public class Freq {
        private static final Integer ONE = new Integer(1);
    
        public static void main(String args[]) {
            Map m = new TreeMap();
    
            // Initialize frequency table from command line
            for (int i=0; i < args.length; i++) {
                Integer freq = (Integer) m.get(args[i]);
                m.put(args[i], (freq==null ? ONE :
                                new Integer(freq.intValue() + 1)));
            }
    
            System.out.println(m.size()+" distinct words detected:");
            System.out.println(m);
        }
    }
    /**
    실행:java Freq if it is to be it is up to me to delegate
    결과:
    8 distinct words detected:
    {be=1, delegate=1, if=1, is=2, it=2, me=1, to=3, up=1}
    */   

4. Hashtable

  • Hashtable Map interface를 implements 한 클래스로서 중복을 허용하지 않는다.
  • Map의 특징인 key와 value의 쌍으로 이루어지며, key 또는 value 값으로써 null을 허용하지 않는다.(HashMap과의 차이점)
  • 아래의 예는 HashTable을 사용한 간단한 예제이다.
    public class HashtableTest
    {
        public static void main(String argv[])
        {
            Hashtable ht = new Hashtable();
            System.out.println(ht.put("aaa", "111"));
            System.out.println(ht.put("bbb", "222"));
            System.out.println(ht.put("aaa", "444"));
            System.out.println(ht.put("ccc", "333"));    
            
            System.out.println("Hashtable size : " + ht.size());
            
            System.out.println("aaa value : " + (String)ht.get("aaa");
            
        }
    }
    /**
    실행:java HashMapTest
    결과:
    null
    null
    111
    null
    Hashtable size : 3
    aaa value : 444
    */   

 

Tistory 태그: ,,,

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

[JAVA] Runtime클래스의 Process exec(String[] cmdarray)  (0) 2008.09.21
[JAVA] java exception  (0) 2008.09.21
[JAVA] java.util.Arrays  (2) 2008.09.21
[JAVA] Job Scheduling in Java  (0) 2008.09.21
[javascript] 테이블의 행추가 행삭제  (0) 2008.09.20

java.util.Arrays

Arrays 클래스는 배열의 정렬, 비교, 검색 등의 기능을 제공한다. Arrays 클래스는 Math 클래스와 같이 객체가 될 수 없다. 따라서 모든 메소드가 static이다.

Arrays 클래스의 유용한 메소드

public static int binarySearch(byte[] a, int key) {

...

public static int binarySearch(double[] a, double key)

해당 배열 a로부터 key 값을 이진 검색하여 key가 있는 위치를 반환한다. 만약 key가 존재하지 않으면 음수를 반환한다.

public static int binarySearch(Object[] a, Object key)

객체형 배열 a로부터 객체 key를 이진 검색하여 key가 있는 위치를 반환한다. 만약 key가 존재하지 않으면 음수를 반환한다.

public static boolean equals(byte[] a, byte[] a2)

...

public static boolean equals(double[] a, double[] a2)

두 배열, a와 a2의 모든 원소가 같으면 true를, 아니면 false를 반환한다.

public static boolean equals(Object[] a, Object[] a2)

두 객체형 배열, a와 a2의 모든 원소가 같으면 true를, 아니면 false를 반환한다.

public static void fill(byte[] a, byte val)

...

public static void fill(double[] a, double val)

public static void fill(Object[] a, Object val)

배열 a의 모든 원소를 val로 채운다(대입한다).

public static void fill(byte[] a, int fromIndex, int toIndex, byte val)

...

public static void fill(double[] a, int fromIndex, int toIndex, double val)

public static void fill(Object[] a, int fromIndex, int toIndex, Object val)

배열 a에서 fromIndex ~ toIndex-1까지의 원소를 val로 채운다.

public static void sort(byte[] a)

...

public static void sort(double[] a)

public static void sort(Object[] a)

주어진 배열 a를 오름차순으로 정렬한다.

public static void sort(byte[] a, int fromIndex, int toIndex)

...

public static void sort(double[] a, int fromIndex, int toIndex)

public static void sort(Object[] a, int fromIndex, int toIndex)

주어진 배열 a에서 fromIndex ~ toIndex-1까지의 원소를 오름차순으로 정렬한다.

알아두기

순차 검색과 이진 검색

순차 검색: 정렬되지 않은 배열에서 키를 검색하는 방법

이진 검색: 정렬된 배열에서 키를 검색하는 방법

순차 검색은 어떤 상황에서도 사용될 수 있는 반면에 이진 검색에 비해 많은 비교를 한다. 이진 검색은 정렬되어 있는 배열에서만 사용되며 순차 검색에 비해 매우 빠르다.

Arrays1.java

import java.util.*;

public class Arrays1{

  public static void main(String[] args){

    int[] a=new int[]{52, 38, 43, 21, 15, 27, 32, 54, 36, 79};

    Arrays.sort(a);                    // 정렬

    for(int i=0;i<a.length;i++)            // 모든 원소를 출력한다.

      System.out.print(a[i]+" ");

    System.out.println("\n54가 있는 위치:" + Arrays.binarySearch(a, 54));

    System.out.println("41이 있는 위치:" + Arrays.binarySearch(a, 41));

  }

}


출력 결과

15 21 27 32 36 38 43 52 54 79

54가 있는 위치: 8

41이 있는 위치: -7


기본 자료(정수, 실수)는 크기의 대소 비교가 가능하지만 객체는 어떻게 대소 비교할 수 있을까? 자바는 직렬화 될 수 있는 객체와 직렬화 될 수 없는 객체로 나누듯이, 비교 가능한 객체와 비교 불가능한 객체로 나눈다. 이것도 인터페이스로 구현된다. 비교 가능한 객체는 Comparable 인터페이스를 구현한다. Comparable을 구현하지 않는 객체는 비교할 수 없는 객체이다.

Comparable 인터페이스는 다음과 같이 정의되어 있다.

// java.lang.Comparable

public interface Comparable {

   public int compareTo(Object o);

}

사용자는 compareTo()를 구현해야된다. compareTo()의 역할은 다음과 같다.

public int compareTo(Object o);

this가 o보다 작으면 음의 정수를, this가 o보다 크면 양의 정수를, 같으면 0을 반환한다.

예를 들어, 사각형 객체들이 있다. 사각형을 서로 비교할 때 비교 기준을 무엇으로 정하면 될까? 넓이로 하면 될 것 같다. 다음 예제를 해보자.

Arrays2.java

class Sagak implements Comparable{     // 사각형 클래스

  private int width;                         // 가로

  private int height;                        // 세로

  Sagak(int width, int height){              // 생성자

    this.width=width;

    this.height=height;

  }

  int getArea(){                            // 면적을 구하는 메소드

    return width*height;

  }

  public int compareTo(Object o){         // CompareTo 오버라이드

Sagak s=(Sagak)o;                   // 사각형 객체로 형 변환

return getArea()-s.getArea();         // 면적의 차를 반환

  }

}

public class Arrays2{

  public static void main(String[] args){

    Sagak a=new Sagak(1,2);              // 넓이가 1*2인 사각형

    Sagak b=new Sagak(2,3);              // 넓이가 2*3인 사각형

    System.out.println(a.compareTo(b));

  }

}


출력 결과

-4


Arrays 클래스의 sort(Object[] a)는 CompareTo 메소드를 사용하여 내부에서 비교 정렬한다. 따라서 정렬하고자 하는 객체는 반드시 Comparable를 구현하고 있어야 될 것이다. 참고로 String 클래스도 Comparable을 구현하고 있다.

int a = new String("abc").compareTo("def");  // a=-3

다음은 사각형 객체 배열을 정렬하는 예제이다.

Arrays3.java

import java.util.*;

public class Arrays3{

  public static void main(String[] args){

    Sagak[] a={new Sagak(2,5),new Sagak(3,3),

               new Sagak(1,2),new Sagak(2,2)};       // 사각형 배열

// 정렬, compareTo 메소드를 사용하여 비교한다.

Arrays.sort(a);

    for(int i=0;i<a.length;i++)                         // 넓이를 출력한다.

      System.out.println(a[i].getArea());

  }

}


출력 결과

2

4

9

10


Tistory 태그: ,

원문 링크 : Job Scheduling in Java

Job Scheduling in Java

by Dejan Bosanac
번역 허태명
03/10/2004

어떤 프로젝트에서 정확히 정해진 시간이나 일정한 시간 간격으로 실행되는 작업이 필요할 수 있다. 이 글에서 우리는 자바 개발자가 표준 Java Timer API를 사용하여 어떻게 이러한 요구사항을 구현할 수 있는지 살펴볼 것이다. 그리고, Java Timer API가 제공하는 기본적인 스케쥴링 시스템 외에 추가적인 기능을 필요로 하는 사람을 위해 오픈 소스 라이브러리인 Quartz에 대해 살펴볼 것이다.

먼저 스케쥴링 작업을 필요로 할 때, 이러한 상황을 인식하는데 도움을 줄 수 있는 일반적인 유스케이스에 대해서 몇 가지 살펴보자. 그리고 나서 우리는 여러분의 기능적 요구에 가장 알맞는 최선의 해결책을 찾아 볼 것이다.

대부분의 비지니스 애플리케이션은 유저들이 필요로 하는 보고서와 통계를 가지고 있다. 시스템에 투자하는 사람들의 일반적인 목적은 대량의 데이터를 수집하고 그것을 미래의 비지니스 계획을 세우는데 도움이 되는 방식으로 보는 것이기 때문에, 이러한 일을 가능하게 해주는 보고서가 없는 시스템은 상상하기 힘들다. 이러한 보고서를 생성하는데 있어서의 문제점은 처리해야할 데이터의 양이 대량이기 때문에, 일반적으로 데이터베이스 시스템에 큰 부하가 걸린다는 것이다. 이러한 부하는 시스템이 보고서를 생성하는 동안, 전체적인 애플리케이션의 성능을 떨어뜨리고 단지 데이터 수집을 위하여 시스템을 사용하는 유저에게도 영향을 끼친다. 또한 유저입장에서 생각한다면, 생성하는데 10분이 걸리는 보고서는 좋은 응답시간의 예가 아니다.

우리는 먼저 실시간으로 실행될 필요가 없는, 캐쉬될 수 있는 종류의 보고서에 대해 살펴볼 것이다. 기쁘게도 대부분의 보고서가 이러한 부류에 들어간다. -- 작년 6월의 어떤 제품 판매에 관한 통계, 또는 1월의 회사 소득 등. 이러한 캐쉬가 가능한 보고서는 간단한 방법으로 해결할 수 있다 : 시스템이 한가할 때 또는 데이터베이스 시스템의 부하가 최소일 때, 보고서를 생성하도록 스케쥴링하면 된다. 여러분이 많은 사무실(모두 같은 시간대에 있는)을 가지고 있는 지역 책 판매자를 위한 애플리케이션을 만든다고 해보자. 여러분은 주당 소득에 대한 보고서(아마도 대량의)를 생성할 필요가 있다. 매주 일요일 밤과 같이 시스템이 사용되지 않는 시간에 데이터베이스에 캐쉬해서 이러한 작업을 스케쥴링 할 수 있다. 이러한 방식으로 구현하면, 판매 담당자는 여러분의 애플리케이션에서 성능상의 문제점을 찾지 못할 것이다. 그리고 회사 관리부는 필요한 모든 데이터를 빠른 시간에 구할 수 있을 것이다.

다음으로 다룰 두번째 예제는 계정 사용기한 만료와 같은 일련의 공지(notification)를 애플리케이션 유저에게 보내는 작업에 관한 것이다. 이것은 유저 데이터에서 날짜 필드를 사용하여 유저의 조건을 검사하는 쓰레드를 생성함으로써 행해질 수 있다. 그러나 이러한 경우 스케쥴러를 사용하는 것이 명백하게 더욱 우아한 해결책이고, 전체적인 시스템 아키텍쳐(아키텍쳐는 중요하다. 그렇지 않은가?)의 측면에서도 더 좋다. 복잡한 시스템에서 여러분은 이러한 종류의 공지를 많이 가지고 있을 것이고, 이외의 다른 많은 경우에도 또한 스케쥴러 시스템이 필요할 것이다. 따라서, 스케쥴링 작업을 필요로 하는 각각의 경우에 대해 해결책을 구현하는 것은 시스템을 변경하고 유지보수하는 것을 더욱 어렵게 할 것이다. 일일이 스케쥴링 작업을 구현하는 대신 여러분은 애플리케이션의 모든 스케쥴링을 담당하는 API를 사용해야만 한다. 이것이 이 글의 나머지 부분에서 다루는 주제이다.

간단한 해결책

자바 애플리케이션에 기본적인 스케쥴러를 구현하기 위해 여러분은 어떤 외부 라이브러리도 필요 없다. J2SE 1.3 이후로 자바는 이러한 목적으로 사용될 수 있는 java.util.Timer, java.util.TimerTask 두 개의 클래스를 포함하고 있다. 먼저 이 API로 가능한 모든 것을 설명해주는 간단한 예제를 만들어보자.

package net.nighttale.scheduling;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class ReportGenerator extends TimerTask {

  public void run() {
    System.out.println("Generating report");
    //TODO generate report
  }

}

class MainApplication {

  public static void main(String[] args) {
    Timer timer  new Timer();
    Calendar date = Calendar.getInstance();
    date.set(
      Calendar.DAY_OF_WEEK,
      Calendar.SUNDAY
    );
    date.set(Calendar.HOUR, 0);
    date.set(Calendar.MINUTE, 0);
    date.set(Calendar.SECOND, 0);
    date.set(Calendar.MILLISECOND, 0);
    // Schedule to run every Sunday in midnight
    timer.schedule(
      new ReportGenerator(),
      date.getTime(),
      1000 * 60 * 60 * 24 * 7
    );
  }
}

이 글의 모든 예제 코드는 글 말미의 링크에서 다운로드 받을 수 있다.

위의 코드는 서두에서 언급한, 시스템이 한가한 시간(예제의 경우 일요일 밤)에 보고서를 생성하도록 스케쥴링하는 예제를 구현하고 있다.

먼저 우리는 실제로 스케쥴링된 작업을 수행하는 "worker" 클래스를 구현해야 한다. 우리의 예제에서 이것은 ReportGenerator 이다. 이 클래스는 java.lang.Runnable을 구현하는 java.util.TimerTask 클래스를 상속받아야 한다. 그리고 나머지 할 일은 보고서를 생성하는 코드를 run() 메소드 안에 오버라이드하는 것 밖에 없다.

우리는 이 객체의 실행을 Timer 클래스의 스케쥴링 메소드 중의 하나를 이용해서 스케쥴링한다. 예제의 경우 최초 실행 날짜와 밀리세컨드 단위의 실행 주기를 인자로 받아들이는 schedule() 메소드를 사용한다.(왜냐하면 우리는 이 보고서를 매주 생성할 것이기 때문이다.)

스케쥴링 기능을 사용할 때, 우리는 스케쥴링 API가 제공하는 리얼타임에 대한 보증을 알아야만 한다. 불행하게도 자바의 특성과 다양한 플랫폼에서의 구현때문에, 각각의 JVM에서의 쓰레드 스케쥴링의 구현은 일치하지 않는다. 그러므로, Timer는 우리의 스케쥴링 작업이 정확한 규정된 시간에 실행될 것이라고 보증할 수 없다. 우리의 스케쥴링 작업은 Runnable 객체로 구현되어 있고 때때로 일정 시간동안 sleep 상태가 된다. 그러면 Timer는 규정된 순간에 이들을 깨운다. 그러나 정확한 실행 시간은 JVM의 스케쥴링 정책과 현재 얼마나 많은 쓰레드가 프로세서를 기다리고 있느냐에 따라 달라진다. 우리의 스케쥴링 작업 실행을 지연시킬 수 있는 두 가지 일반적인 경우가 있다. 첫째, 많은 수의 쓰레드가 실행되기를 기다리고 있는 경우이다; 둘째, 가비지 콜렉션의 활동에 의해 지연되는 경우가 있다. 이러한 영향들은 다른 JVM에서 스케쥴러를 실행 또는 가비지 콜렉터의 옵션 튜닝과 같은 여러가지 기법을 사용함으로써 최소화될 수 있다. 그러나 그것은 이 글의 주제를 벗어나는 것이다.

다시 본론으로 돌아오자. Timer 클래스에는 두 개의 다른 스케쥴링 메소드 그룹이 있다: 고정된 딜레이로 스케쥴링하는 schedule() 메소드와 고정된 비율로 스케쥴링하는 scheduleFixedRate() 메소드가 그것이다. 첫번째 그룹의 메소드들을 사용할 때, 각 작업 실행의 딜레이는 다음 작업 실행으로 전달될 것이다. 후자 그룹의 경우 딜레이를 최소화하면서 모든 연속된 작업 실행은 최초 작업 실행의 시간에 맞춰 스케쥴링 될 것이다. 어떤 메소드를 사용하느냐는 여러분의 시스템에 어떤 파라미터가 더 중요하느냐에 달려 있다.

매우 중요한 것이 한 가지 더 있다: 각 Timer 객체는 쓰레드를 백그라운드로 시작한다. 이러한 방식은 J2EE 애플리케이션 서버와 같은 환경에서는 바람직하지 않을 것이다. 왜냐하면 이러한 쓰레드들이 컨테이너 영역 내에 있지 않기 때문이다.

평범한 것을 넘어서

지금까지 애플리케이션에서 어떻게 스케쥴링을 하는지 살펴 보았고, 이것은 간단한 요구사항에서는 충분하다. 그러나 고급 유저와 복잡한 요구사항을 위해서는 유용한 스케쥴링을 지원하기 위해 더 많은 기능들을 필요로 한다. 이러한 경우 두 가지 일반적인 해결책이 있다. 첫번째는 자신이 필요로 하는 기능을 가지고 있는 스케쥴러를 만드는 것이다; 두번째는 필요로 하는 요구사항을 충족하는 프로젝트를 찾아내는 것이다. 시간과 자원을 절약할 수 있고 다른 누군가의 노력을 중복해야 할 필요가 없기 때문에, 두번째 해결책이 대부분의 경우에 있어서 더욱 적합할 것이다.

이러한 요구사항은 우리를 단순한 Timer API 이상의 훨씬 뛰어난 장점들을 가진 오픈 소스 작업 스케쥴링 시스템인 Quartz 로 유도한다.

Quartz의 첫번째 장점은 영속성이다. 만약 여러분의 작업이 앞서의 예제와 같이 "정적"이라면 영속성 지원은 필요 없을 것이다. 그러나 종종 어떤 조건이 충족됐을 때 "동적"으로 수행되는 작업을 필요로 할 때도 있다. 그리고 이러한 작업들이 시스템 재시작(또는 시스템 다운) 사이에도 실행되야 할 때가 있다. Quartz는 비 영속성과 영속성 작업 모두를 제공한다. 영속성 작업의 상태는 데이터베이스에 저장될 것이다. 따라서 이러한 작업들이 실행되지 않는 경우가 없을 거라고 확신할 수 있다. 영속성 작업은 시스템에 추가적인 성능 감소를 유발하기 때문에 주의깊게 사용해야만 한다.

Timer API는 원하는 실행시간을 단순하게 설정할 수 있는 메소드가 부족하다. 위 예제에서 본 대로, 실행시간을 설정하기 위해서 할 수 있는 것은 시작일자와 반복주기를 설정하는 것 밖에 없다. 분명히, 유닉스의 cron을 사용해 본 사람들은 스케쥴러를 그와 유사하게 설정할 수 있기를 바랄 것이다. Quartz는 원하는 시간, 날짜를 유연한 방법으로 설정할 수 있게 해주는 org.quartz.CronTrigger를 정의하고 있다.

개발자는 자주 한 가지 이상의 기능을 필요로 한다: 작업의 이름에 의한 작업의 관리와 조직화. Quartz에서 당신은 이름이나 그룹에 의해 원하는 작업을 찾아낼 수 있다. 이것은 스케쥴된 작업이 많은 환경에서 큰 도움이 될 것이다.

이제 보고서 생성 예제를 Quartz를 사용하여 구현하고 라이브러리의 기본적인 기능들에 대해 설명할 것이다.

package net.nighttale.scheduling;

import org.quartz.*;

public class QuartzReport implements Job {

  public void execute(JobExecutionContext cntxt)
    throws JobExecutionException {
      System.out.println(
        "Generating report - " +
cntxt.getJobDetail().getJobDataMap().get("type")
      );
      //TODO Generate report
  }

  public static void main(String[] args) {
    try {
      SchedulerFactory schedFact 
       new org.quartz.impl.StdSchedulerFactory();
      Scheduler sched  schedFact.getScheduler();
      sched.start();
      JobDetail jobDetail 
        new JobDetail(
          "Income Report",
          "Report Generation",
          QuartzReport.class
        );
      jobDetail.getJobDataMap().put(
                                "type",
                                "FULL"
                               );
      CronTrigger trigger  new CronTrigger(
        "Income Report",
        "Report Generation"
      );
      trigger.setCronExpression(
        "0 0 12 ? * SUN"
      );
      sched.scheduleJob(jobDetail, trigger);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Quartz는 Job, Trigger라는 두 개의 기본 추상 계층을 정의한다. Job은 실제 실행되는 작업의 추상 계층이고, Trigger는 언제 작업이 실행되어야 하는지를 나타내는 추상 계층이다.

Job은 인터페이스이다. 그래서 우리가 해야할 일은 클래스가 org.quartz.Job(또는 나중에 살펴보게 될 org.quartz.StatefulJob) 인터페이스를 구현하도록 하고 execute() 메소드를 오버라이드하는 것 뿐이다. 예제에서 java.util.Map의 변형된 구현인 jobDataMap 어트리뷰트를 통해 어떻게 Job에게 파라미터를 전달할 수 있는지 살펴 봤다. 상태가 있는 작업, 또는 비 상태 작업 중 어떤 것을 구현하느냐 결정하는 것은 스케쥴링 작업의 실행동안 이러한 파라미터들을 변경하기를 원하느냐 아니냐를 결정하는 문제이다. Job 인터페이스를 구현한다면 모든 파라미터들은 작업이 최초로 스케쥴링 되는 순간에 저장된다. 그리고 이후의 변경은 모두 버려진다. execute() 메소드 내에서 StatefulJob의 파라미터를 변경한다면, 작업이 다음에 새로 스케쥴링 될 때 이 새로운 값이 전달될 것이다. 고려해야할 한 가지 중요한 사항은 StatefulJob 인터페이스를 구현한 작업들은 실행되는 동안 인자들이 변할 수 있기 때문에 동시에 실행될 수 없다는 것이다.

Trigger에는 두 가지의 기본적인 Trigger가 있다: SimpleTrigger 와 CronTrigger. SimpleTrigger는 기본적으로 Timer API가 제공하는 것과 같은 기능을 제공한다. 작업이 시작된 이후에 정해진 간격으로 반복해서 실행되는 경우, SimpleTrigger를 써야 한다. SimpleTrigger는 시작일, 종료일, 반복횟수, 실행 주기를 정의할 수 있다.

위의 예제에서는 더욱 사실적인 바탕에서 작업을 스케쥴링할 수 있는 유연성때문에 CronTrigger를 사용했다. CrinTrigger 사용함으로써 "매주 평일 오후 7시" 또는 "토요일과 일요일 매 5분마다"와 같은 스케쥴링 작업을 할 수 있다. 더 이상 cron에 관해 설명하지는 않을 것이다. cron에 관한 세부사항은 CronTrigger의 Javadoc을 참조하기 바란다.

위의 예제를 실행하기 위해 클래스패쓰에 기본적인 Quartz의 설정을 하는 quartz.properties 파일을 필요로 한다. 만약 파일이름을 다르게 사용하기를 원한다면, 파일이름을 StdScheduleFactory 생성자에 인자로 전달해야만 한다. 아래에 최소한의 프로퍼티들만 설정한 파일의 예제가 있다:

#
# Configure Main Scheduler Properties 
#

org.quartz.scheduler.instanceName = TestScheduler
org.quartz.scheduler.instanceId = one

#
# Configure ThreadPool 
#

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount =  5
org.quartz.threadPool.threadPriority = 4

#
# Configure JobStore 
#

org.quartz.jobStore.misfireThreshold = 5000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

표준 Timer API에 비해 Quartz가 가진 또 다른 장점은 쓰레드 풀의 사용이다. Quartz는 작업 실행을 위한 쓰레드를 얻기 위해 쓰레드 풀을 사용한다. 쓰레드 풀의 크기는 동시에 실행될 수 있는 작업의 수에 영향을 미친다. 실행해야할 작업이 있지만 쓰레드 풀에 남아 있는 쓰레드가 없다면, 작업은 여분의 쓰레드가 생길 때까지 sleep 상태가 될 것이다. 시스템에서 얼마나 많은 쓰레드를 사용할 지는 매우 결정하기 어려운 문제이고, 실험적으로 결정하는 것이 가장 좋다. 쓰레드 풀 크기의 기본 값은 5이고 수천개의 작업을 다루지 않는다면 이것은 충분할 것이다. Quartz 자체에서 구현한 쓰레드 풀이 있지만, 다른 쓰레드 풀의 구현이 있다면 그것을 사용하는데 제약을 받지는 않는다.

이제 JobStore에 관해 살펴 보자.JobStore는 Job과 Trigger에 관한 모든 데이터를 보존한다. 따라서 작업에 영속성을 부여할 것인지 아닌지 결정하는 것은 어떤 JobStore를 사용하느냐에 달려 있다. 예제에서 우리는 org.quartz.simpl.RAMJobStore를 사용했다. 이것은 모든 데이터는 메모리에 저장될 것이고 그러므로 비 영속성이라는 것을 의미한다. 따라서 애플리케이션이 다운되면 스케쥴링 작업에 관한 모든 데이터는 사라질 것이다. 어떤 상황에서 이것은 바람직한 방식이다. 그러나 데이터를 보존하고 싶다면 애플리케이션이 org.quartz.simpl.JDBCJobStoreTX(또는 org.quartz.simpl.JDBCJobStoreCMP)를 사용하도록 설정해야 한다.JDBCJobStoreTX는 좀 더 많은 설정 파라미터를 필요로 하고 그것은 아래 예제에서 설명할 것이다.

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_

#
# Configure Datasources 
#

org.quartz.dataSource.myDS.driver = org.postgresql.Driver
org.quartz.dataSource.myDS.URL = jdbc:postgresql:dev
org.quartz.dataSource.myDS.user = dejanb
org.quartz.dataSource.myDS.password =
org.quartz.dataSource.myDS.maxConnections  5

Quartz와 관계형 데이터베이스를 성공적으로 사용하기 위하여 먼저 Quartz가 필요로 하는 테이블을 생성할 필요가 있다. 적절한 JDBC 드라이버를 가지고 있다면 어떤 데이터베이스 서버도 사용 가능하다. docs/dbTables 폴더에서 필요한 테이블을 생성하는 초기화 스크립트를 찾을 수 있다.

테이블을 생성한 후에 표준 SQL 쿼리를 특정 RDBMS의 SQL 문법에 맞게 변경해주는 위임(delegate) 클래스를 선언해야 한다. 예제에서 우리는 PostgreSQL을 데이터베이스 서버로 선택했고 따라서 org.quartz.impl.jdbcjobstore.PostgreSQLDelegate 클래스를 위임 클래스로 설정했다. 당신이 사용하는 특정 데이터베이스 서버를 위해 어떤 위임 클래스를 사용해야 하는지에 관한 정보는 Quartz 문서를 참조하기 바란다.

tablePrefix 파라미터는 데이터베이스에서 Quartz 테이블이 사용할 접두어를 정의한다. 디폴트는 QRTZ_ 이다. 이런 방식으로 데이터베이스의 나머지 테이블과 구분할 수 있다.

사용하는 매 JDBC store마다 어떤 datasource를 사용할 것인지 정의할 필요가 있다. 이것은 일반적인 JDBC의 설정이기 때문에 여기서 더 이상 설명하지는 않을 것이다.

Quartz의 뛰어난 점은 이러한 설정을 변경한 후에 보고서 생성 예제의 코드를 단 한줄도 변경하지 않고, 데이터를 데이터베이스에 저장할 것이라는 것이다.

Advanced Quartz

지금까지 프로젝트에 Quartz를 사용하기 위한 좋은 기초가 될 수 있는 기본적인 것에 대해 살펴 보았다. 이외에도 Quartz 라이브러리는 당신의 수고를 크게 덜어줄 수 있는 뛰어난 아키텍쳐를 가지고 있다.

Quartz는 기본적으로 제공하는 것 외에 애플리케이션의 문제를 해결하는데 도움이 되는 뛰어난 아키텍쳐를 가지고 있다. 그 중 중요한 기능 중의 하나는 listener 이다: 이것은 시스템에 어떤 이벤트가 발생할 때 호출된다. 세 가지 종류의 리스너가 있다: JobListener, TriggerListener, SchedulerListener 이다.

리스너는 시스템에 무언가 이상이 생겨서 이에 대한 공지나 알림 기능을 원할 때 특히 유용하다. 예를 들어 보고서 생성 중에 에러가 발생하면 개발 팀에게 이를 알리는 우아한 방법은 E-mail이나 SMS를 보내는 JobListener 를 만드는 것이다.

JobListener 는 더 흥미로운 기능을 제공한다. 시스템 자원의 가용성에 크게 의존하는 일(그다지 안정적이지 못한 네트워크와 같은)을 다루는 작업을 상상해보라. 이러한 경우 작업이 실행될 때 자원이 사용 불가능하다면 이를 재실행 시키는 리스너를 만들 수 있다.

Quartz는 또한 Trigger 가 잘못 실행되거나 스케쥴러가 다운되서 실행되지 않았을 때의 상황을 다룰 수 있다. Trigger 클래스의 setMisfireInstruction() 메소드를 사용함으로써 오실행에 관한 처리를 설정할 수 있다. 이 메소드는 오실행 명령 타입을 인자로 받아들인고, 그 값은 다음중의 하나가 될 수 있다:

  • Trigger.INSTRUCTION_NOOP: 아무 일도 하지 않는다.
  • Trigger.INSTRUCTION_RE_EXECUTE_JOB: 즉시 작업을 실행한다.
  • Trigger.INSTRUCTION_DELETE_TRIGGER: 오실행한 작업을 삭제한다.
  • Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE: 작업이 완료를 선언한다.
  • Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE: 작업을 위한 모든 trigger의 완료를 선언한다.
  • Trigger.MISFIRE_INSTRUCTION_SMART_POLICY: 특정 Trigger의 구현에 가장 알맞은 오실행 처리 명령을 선택한다.

CronTrigger와 같은 Trigger 구현은 유용한 오실행 처리 명령을 새로 정의할 수 있다. 이것에 관한 더 많은 정보는 이 클래스들의 Javadoc을 참고하기 바란다. TriggerListener를 사용함으로써 오실행이 발생했을 경우 취할 액션에 관해 더 많은 제어권을 가질 수 있다. 또한 트리거의 실행이나 종료와 같은 트리거 이벤트에 반응하기 위해 이것을 사용할 수 있다.

SchedulerListener 는 스케쥴러 종료나 작업과 트리거의 추가나 제거와 같은 전체적인 시스템 이벤트를 다룬다.

여기서 우리는 보고서 생성 예제를 위한 간단한 JobListener 를 보여줄 것이다. 먼저 JobListener 인터페이스를 구현하는 클래스를 작성해야 한다.

package net.nighttale.scheduling;

import org.quartz.*;


public class MyJobFailedListener implements JobListener {

  public String getName() {
    return "FAILED JOB";
  }

  public void jobToBeExecuted
    (JobExecutionContext arg0) {
  }


  public void jobWasExecuted(
    JobExecutionContext context,
    JobExecutionException exception) {

    if (exception != null) {
      System.out.println(
        "Report generation error"
      );
      // TODO notify development team
    }	
  }
}

그리고 예제의 main 메소드에 다음을 추가한다:

sched.addGlobalJobListener(new MyJobFailedListener());

이 리스너를 스케쥴러 작업 리스너의 전체 목록에 추가함으로써 리스너는 모든 작업의 이벤트에 대해 호출 될 것이다. 물론 리스너를 특정 작업에 대해서만 설정할 수도 있다. 그렇게 하기 위해 ScheduleraddJobListeners() 메소드를 사용해야 한다. 그리고 JobDetailaddJobListener() 메소드를 사용해서 등록된 리스너를 리스너의 작업 목록에 추가해라. 이 때 리스너 이름을 파라미터로 사용한다. 리스너 이름은 리스너의 getName() 메소드의 리턴값을 말한다.

sched.addJobListener(new MyJobFailedListener());
jobDetail.addJobListener("FAILED JOB");

리스너가 정말로 동작하는지 테스트하기 위해, 다음을 보고서 생성 작업의 execute() 메소드 안에 추가한다.

throw new JobExecutionException();

작업이 실행된 후 리스너의 jobWasExecuted() 메소드가 실행되고, 예외가 발생한다. 예외는 null 이 아니기 때문에 "Report generation error" 메세지를 화면에서 볼 수 있을 것이다.

리스너에 관한 마지막 사항은 애플리케이션의 성능을 떨어뜨릴 수 있기 때문에 시스템에서 사용되는 리스너의 갯수에 유의해야한다는 것이다.

Quartz의 기능을 확장할 수 있는 방법이 한 가지 더 있다. 그것은 플러그 인을 이용하는 것이다. 플러그 인은 실질적으로 여러분이 필요한 어떤 일도 할 수 있다; 단지 해야할 일은 org.quartz.spi.SchedulerPlugin 인터페이스를 구현하는 것 뿐이다. 이 인터페이스는 구현해야할 두 개의 메소드를 정의한다 -- 하나는 초기화(스케쥴러 객체를 파라미터로 받는)를 위한 것이고, 또 하나는 종료를 위한 것이다. 나머지는 모두 여러분에게 달려 있다. SchedulerFactory 가 플러그 인을 사용하도록 하기 위해 quartz.properties 파일에 플러그 인 클래스와 몇 가지 옵션 설정 파라미터(플러그인를에서 필요로 하는)를 추가한다. In order to make SchedulerFactory use a certain plug-in, all you have to do is to add a line in the properties file (quartz.properties) with the plug-in class and a few optional configuration parameters (which depend on the particular plug-in). There are a few plug-ins already in Quartz itself. One is the shutdownHook, which can be used to cleanly shut down the scheduler in case the JVM terminates. To use this plug-in, just add the following lines in the configuration file:

org.quartz.plugin.shutdownHook.class = 
   org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownHook.cleanShutdown = true
어떤 환경에서도 융통성 있는

지금까지 Standalone 애플리케이션에서 Quartz를 사용하는 것을 살펴 봤다. 이제 Quartz 인터페이스를 자바 개발자의 가장 보편적인 환경 하에서 어떻게 사용하는지 살펴 보자.

RMI

RMI를 사용하는 분산 애플리케이션에서 Quartz는 지금까지 본 것과 마찬가지로 간단하다. 차이점은 설정 파일 밖에 없다.

두 가지 필수 단계가 있다: 먼저 Quratz를 우리의 request를 다루는 RMI 서버로 설정하는 것이다. 그러고 나면 단지 일반적인 방식으로 사용하기만 하면 된다.

이 예제의 소스 코드는 실질적으로 첫번째 예제와 동일하지만, 우리는 이것을 두 부분으로 나눌 것이다: 스케쥴러 초기화와 스케쥴러 사용.

package net.nighttale.scheduling.rmi;

import org.quartz.*;

public class QuartzServer {

  public static void main(String[] args) {
	
    if(System.getSecurityManager() != null) {
      System.setSecurityManager(
        new java.rmi.RMISecurityManager()
      );
    }
	   
    try {
      SchedulerFactory schedFact =
       new org.quartz.impl.StdSchedulerFactory();
      Scheduler sched = schedFact.getScheduler();
      sched.start();	
    } catch (SchedulerException se) {
      se.printStackTrace();
    }
  }
}

위에서 볼 수 있듯이, 스케쥴러 초기화 코드는 security manager를 설정하는 부분을 포함하고 있다는 것 외엔 다른 것과 다를 바 없다. 중요한 것은 설정 파일(quartzServer.properties)안에 있다. 그것은 다음과 같다:

#
# Configure Main Scheduler Properties 
#
org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.rmi.export = true
org.quartz.scheduler.rmi.registryHost = localhost
org.quartz.scheduler.rmi.registryPort = 1099
org.quartz.scheduler.rmi.createRegistry = true

#
# Configure ThreadPool 
#

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 4

#
# Configure JobStore 
#

org.quartz.jobStore.misfireThreshold = 5000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

강조된 부분을 주목하라. 이것은 이 스케쥴러가 RMI를 통하여 인터페이스를 반출하고 RMI registry를 실행하기 위해 파라미터들을 제공해야 한다는 것을 보여준다.

서버를 성공적으로 배치하기 위해 몇 가지 할 일이 더 있다. 그것들은 모두 RMI를 통하여 객체를 반출하기 위한 전형적인 작업이다. 먼저 rmiregistry 를 시작할 필요가 있다. 이것은 다음과 같이 호출함으로써 이루어진다: 유닉스 시스템에서는

   rmiregistry &

또는 윈도우 플랫폼에서는

   start rmiregistry

다음에 QuartzServer 클래스를 다음과 같은 옵션으로 시작한다:

java  -Djava.rmi.server.codebase
   file:/home/dejanb/quartz/lib/quartz.jar
   -Djava.security.policyrmi.policy
   -Dorg.quartz.propertiesquartzServer.properties
   net.nighttale.scheduling.rmi.QuartzServer

이제 이러한 파라미터들을 좀 더 자세하게 살펴보자. Quartz의 Ant 빌드 태스크는 필요한 RMI 클래스를 클라이언트가 codebase로 가리키도록 이 클래스들을 생성하는 rmic 호출을 포함하고 있다. 그러기 위해 빌드 파일에 -Djava.rmi.server.codebase 파라미터로 시작하도록 설정해야 한다:추가적으로 quartz.jar 의 완전한 경로도 포함되어야 한다.(물론 이것은 라이브러리의 URL이 될 수도 있다.)

분산 시스템에서 중요한 이슈는 보안이다; 그러므로 RMI는 보안 정책을 사용하도록 강제할 것이다. 예제에서는 모든 권한을 허용하는 기본 정책(rmi.policy)을 사용했다.

grant {
  permission java.security.AllPermission;
};

실제 적용할 때는 시스템의 보안 요구사항에 따라 정책을 변경해야 한다.

이제 스케쥴러는 RMI를 통해 Job을 받아들일 준비가 되었다. 이제 클라이언트 코드를 살펴보자.

package net.nighttale.scheduling.rmi;

import org.quartz.*;
import net.nighttale.scheduling.*;

public class QuartzClient {

  public static void main(String[] args) {
    try {
      SchedulerFactory schedFact =
       new org.quartz.impl.StdSchedulerFactory();
      Scheduler sched = schedFact.getScheduler();
      JobDetail jobDetail = new JobDetail(
        "Income Report",
        "Report Generation",
        QuartzReport.class
      );
		
      CronTrigger trigger = new CronTrigger(
        "Income Report",
        "Report Generation"
      );
      trigger.setCronExpression(
        "0 0 12 ? * SUN"
      );
      sched.scheduleJob(jobDetail, trigger);
    } catch (Exception e) {
      e.printStackTrace();
    }
   }
}

위에 나온 바와 같이 클라이언트 소스는 이전 예제와 동일하다. 물론 여기에도 quartzClient.properties 의 설정은 다르다. 추가해야 할 것은 이 스케쥴러가 RMI 클라이언트(proxy)이고, 서버를 찾을 registry의 위치를 설정하는 것 뿐이다.

# Configure Main Scheduler Properties  

org.quartz.scheduler.instanceName = Sched1
org.quartz.scheduler.rmi.proxy = true
org.quartz.scheduler.rmi.registryHost = localhost
org.quartz.scheduler.rmi.registryPort = 1099

나머지는 모두 서버 측에서 이루어지기 때문에 클라이언트 측에 이외의 다른 어떤 설정도 필요하지 않다. 사실 다른 설정이 있다해도 이것은 무시될 것이다. 중요한 것은 스케쥴러의 이름은 클라이언트와 서버의 설정이 일치해야 한다는 것이다.(예제의 경우 Shed1) 그러면 시작하기 위한 준비는 끝났다. 단지 리다이렉트된 프로퍼티 파일로 클라이언트를 시작하기만 하면 된다:

java -Dorg.quartz.properties
       quartzClient.properties
       net.nighttale.scheduling.rmi.QuartzClient

이제 서버 콘솔에서 첫번째 예제와 같은 결과를 볼 수 있을 것이다.

웹과 엔터프라이즈 환경

웹이나 엔터프라이즈 환경의 솔류션을 개발하고 있다면, 스케쥴러를 시작하기 위한 적절한 곳은 어디인지 의문이 생길 것이다. 이것을 위해 Quartz는 org.quartz.ee.servlet.QuartzInitializerServlet를 제공한다. 할 일은 단지 web.xml 파일에 다음 설정을 추가하는 것 뿐이다:


  
   QuartzInitializer
  
  
   Quartz Initializer Servlet
  
  
   org.quartz.ee.servlet.QuartzInitializerServlet
  
  
   1
  

Job을 EJB 메소드에서 호출하고 싶다면, org.quartz.ee.ejb.EJBInvokerJob 클래스를 JobDetail 에 전달해야 한다. 이러한 기법을 보여주기 위해, ReportGenerator를 세션빈으로 구현하고 서블릿으로부터 generateReport() 메소드를 호출할 것이다.

package net.nighttale.scheduling.ee;

import java.io.IOException;

import javax.servlet.*;
import net.nighttale.scheduling.Listener;
import org.quartz.*;
import org.quartz.ee.ejb.EJBInvokerJob;
import org.quartz.impl.StdSchedulerFactory;

public class ReportServlet implements Servlet {

  public void init(ServletConfig conf)
    throws ServletException {
    JobDetail jobDetail = new JobDetail(
      "Income Report",
      "Report Generation",
      EJBInvokerJob.class
    );
    jobDetail.getJobDataMap().put(
      "ejb",
      "java:comp/env/ejb/Report"
    );
    jobDetail.getJobDataMap().put(
      "method",
      "generateReport"
    );
    Object[] args = new Object[0];
    jobDetail.getJobDataMap().put("args", args);
    CronTrigger trigger = new CronTrigger(
      "Income Report",
      "Report Generation"
    );
    try {
      trigger.setCronExpression(
        "0 0 12 ? * SUN"
      );
      Scheduler sched =
       StdSchedulerFactory.getDefaultScheduler();
      sched.addGlobalJobListener(new Listener());
      sched.scheduleJob(jobDetail, trigger);
      System.out.println(
        trigger.getNextFireTime()
      );
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public ServletConfig getServletConfig() {
    return null;
  }

  public void service(ServletRequest request,
                      ServletResponse response)
    throws ServletException, IOException {
  }

  public String getServletInfo() {
    return null;
  }

  public void destroy() {
  }
}

위에 나온 바와 같이, job에 전달할 필요가 있는 파라미터가 3개가 있다.

  • ejb: 엔터프라이즈 빈의 JNDI 이름.
  • method: 실제 호출되야 할 메소드의 이름.
  • args: 메소드의 인자로 전달되야 할 객체의 배열.

나머지는 Quartz의 일반 사용법과 다른 것이 없다. 간단하게 하기 위해 이 예제를 서블릿의 초기화 메소드에 위치시켰지만, 물론 애플리케이션의 어떠한 위치에 놓아도 상관 없다. Quartz를 성공적으로 실행하기 위해 웹 애플리케이션에 이 EJB를 등록할 필요가 있다. 일반적으로 web.xml 파일에 다음을 추가하면 된다.


  ejb/Report
  Session
  net.nighttale.scheduling.ee.ReportHome
  net.nighttale.scheduling.ee.Report
  ReportEJB

어떤 애플리케이션 서버(Orion과 같은)는 유저 쓰레드를 생성하기 위한 권한을 설정해야 할 필요가 있다. 그러므로 -userThread와 같은 옵션으로 실행해야 할 것이다.

지금까지 살펴 본 것이 Quartz의 엔터프라이즈 기능의 전부는 아니다. 그러나 처음 시작할 때 참고할 만할 것이다. 지세한 정보를 원한다면 Quartz's Javadocs 를 참고하기 바란다.

Web Services

Quartz는 현재 웹 서비스를 위해 내장된 지원은 없지만, XML-RPC 을 통하여 스케쥴러 인터페이스를 반출하는 플러그 인을 찾을 수 있다. 설치 절차는 간단하다. 시작하기 위해 플러그 인 소스를 Quartz 소스 폴더에 압축을 풀고 다시 빌드해야만 한다. 플러그 인은 Jakarta XML-RPC 라이브러리에 의존하므로, 그것이 클래스패쓰에 잡혀 있는지 확인해야 한다. 다음에 프로퍼티 파일에 아래의 내용을 추가한다.

org.quartz.plugin.xmlrpc.class = org.quartz.plugins.xmlrpc.XmlRpcPlugin
org.quartz.plugin.xmlrpc.port = 8080

이제 스케쥴러는 XML-RPC를 통해 사용할 수 있는 준비가 되었다. 이러한 방식으로 PHP나 Perl같은 다른 언어, 또는 RMI가 적합한 해결책이 아닌 분산 환경에서, Quartz의 기능 중의 일부를 사용할 수 있다.

요약

지금까지 자바에서 스케쥴링을 구현하는 두 가지 방법에 대해 살펴 봤다. Quartz는 매우 강력한 라이브러리지만, 간단한 요구사항에 대해서는 Timer API가 시간을 절약하고 시스템에 불필요한 복잡성을 피할 수 있게 해 준다. 스케쥴링이 필요한 작업이 그다지 많지 않고, 프로세스에서 실행시간이 잘 알려져 있을 경우(그리고 불변일 경우) Timer API를 사용할 것을 고려해라. 이러한 상황에서 시스템 중지나 다운 때문에 스케쥴링 작업들을 잃어버릴 것에 대해 걱정할 필요는 없다. 더욱 복잡한 요구사항을 위해서는 Quartz가 우아한 해결책이다. 물론 언제든지 Timer API를 기반으로 당신 자신의 프레임워크를 만들 수 있다. 그러나 이미 당신이 필요로 하는 모든 기능(그리고 그 이상의 기능)을 제공하는 훌륭한 해결책이 존재하는데 왜 그런 귀찮을 일을 하려는가?

한 가지 주목할 만한 이벤트는 IBM과 BEA이 제출한 "Timer for Application Servers" JSR 236 이다. 이 스펙은 스케쥴링에 관련된 표준 API가 부족한 J2EE 환경에서 타이머를 위한 API를 만드는 데 촛점을 두고 있다. IBM's developerWorks site에서 스펙에 관련된 보다 자세한 사항을 찾을 수 있다. 이 스펙은 봄에 일반 사용자에게 공개될 것이다.

샘플 코드

ReportGenerator.zip

Dejan Bosanac은 DNS 유럽의 소프트웨어 개발자 이다.

 

여기서 e는 테이블

e는 테이블 id 를 넣으면 됨

function addRow(e)
{
num++;

//num은 들어갈 행번호 2면 3번째줄에 추가
var oRow = e.insertRow(num);

///////////////높이

oRow.height ="10";

//들어갈 td 만큼 만듬!!
var oCell1 = oRow.insertCell();
var oCell2 = oRow.insertCell();
var oCell3 = oRow.insertCell();
////// insertCell() 자체가 <td></td>이기때문에 적어줄필요없이 td 안의 내용만 적어주면됨

그러나 td자체에 스타일주기가 좀 까다로움

oCell1.innerHTML = "<p style='font-family:굴림;font-size:12'>&nbsp;<input type='text' name='book"+num+"' style='width:96%'>

</p>";
oCell2.innerHTML = "<p style='font-family:굴림;font-size:12'>&nbsp;<input type='text' name='writer"+num+"' style='width:96%'></p>";
oCell3.innerHTML = "<p style='font-family:굴림;font-size:12'>&nbsp;<input type='text' name='publish"+num+"' style='width:96%'></p>";
}

//// 테이블 행 삭제
function delRow(e)
{
var oTr = e.deleteRow(num);
num--;
}

 

Tistory 태그: ,

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

[JAVA] java.util.Arrays  (2) 2008.09.21
[JAVA] Job Scheduling in Java  (0) 2008.09.21
[javascript] 금액표시/숫자여부체크/replace  (0) 2008.09.20
[java] 날짜 구하기  (0) 2008.09.20
[java] 파일과 디렉토리  (1) 2008.09.20

======== 숫자 여부 체크 ========

if(isNaN(result))
{
  alert("수량과 단가는 숫자만 적으실수 있습니다");   
}else{
  requestCost.value = result;
}

======== 숫자 3자리마다 컴마찍기 =========

function formatNumber(tx)
{
//화페3자리 콤마
   s = tx.value;
//   alert(s);
   s=s.replace(/\D/g,"");
   l=s.length-3;
   while(l > 0) {
     s=s.substr(0,l)+","+s.substr(l);
     l-=3;
   }
   tx.value = s;
}

==============  replace ================

음..자바스크립트에서 333,333,333 을 333333333으로 만드려고 asp처럼 단순히 replace 하면 333333,333 이런값을 반환한다.
따라서 정규 표현식 사용해야함...
var re = /,/g;
var txt = "333,333,333";
txt = txt.replace(re,"");
요렇게 한후 alert(txt) 찍어보면 333333333 나옴
*)
i(대/소문자를 구분하지 않음)
g(발생할 모든 패턴에 대한 전역 검색)
gi(전역 검색, 대/소문자를 구분하지 않음)

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

[JAVA] Job Scheduling in Java  (0) 2008.09.21
[javascript] 테이블의 행추가 행삭제  (0) 2008.09.20
[java] 날짜 구하기  (0) 2008.09.20
[java] 파일과 디렉토리  (1) 2008.09.20
[java] 객체 풀의 동작 원리  (0) 2008.09.20

+ Recent posts