01.JAVA/Java2008. 11. 12. 17:22
반응형

2004년 3월 25일자 테크팁 예외 처리의 Best Practices에서 예외 처리를 위한 몇 가지 선진 기법을 설명한 바 있다. 이번 테크팁에서는 예외를 처리하는 또 다른 방법에 관해 배우게 될 것인데, J2SE 5.0에 추가된 UncaughtExceptionHandler가 바로 그것이다.

이름에서 알 수 있듯이 UncaughtExceptionHandler는 캐치되지 않은 예외를 처리하는 방법인데, 좀더 구체적으로 얘기하면 캐치되지 않은 런타림 예외를 처리하는 것이다. 자바 컴파일러는 모든 비(非) 런타임 예외를 처리할 것을 요구하며, 그렇지 않을 경우 프로그램은 컴파일되지 않는다. 여기서 '처리한다'는 것은 예외가 선언하는 메소드의 throws 절에서 선언되거나 try-catch 블록의 catch 절에서 캐치되는 것을 의미한다.

그럼 예시를 위해 다음 두 가지 예외를 살펴보기로 하자: FileNotFoundExceptionArithmeticException. String이나 File 인자로 FileReader 생성자를 호출할 때, 제공된 장소가 유효한 정상 파일을 가리키지 않을 경우 FileNotFoundException이 throw된다. 컴파일러는 이 생성자들 중 하나를 호출할 때 throw된 예외를 처리할 것을 요구한다.
   FileReader input;
   String filename = ...;
   try {
     input = new FileReader(filename);
   } catch (FileNotFoundException e) {
     processMissingFile(filename, e);
   }
이와 대조적으로, ArithmeticException은 일종의 런타임 예외라 할 수 있는데, 자바 프로그래밍 언어 스펙은(컴파일러도 마찬가지로) 그 런타임 예외를 처리할 것을 요구하지 않는다. 따라서 10에서 0까지의 수를 100으로 나누는 다음 루프의 경우 루프를 통과하는 최종 패스에 ArithmeticException을 throw한다:
   for (int i=10; i >= 0; i--) {
     int div = 100 / i;
   }

   Exception in thread "main" java.lang.ArithmeticException: / 
   by zero
        at Test.main(Test.java:4)
기본 Uncaught Exception Handler의 역할은 스택 트레이스를 프린트하는 것인데, 보통 이 기본값 동작으로 충분하지만 꼭 그렇지 않은 경우도 있다. 스택 트레이스를 시스템 콘솔에 덤프하는 대신 팝업 창에 트레이스를 표시한다고 생각해보자. 이럴 때는 자체의 기본 Uncaught Exception Handler를 설치하면 이 작업을 수행할 수 있다.

Uncaught Exception Handler 설치에는 최소 세 가지 방법이 사용되고 있다. 첫째는 ThreadsetUncaughtExceptionHandler() 메소드를 호출하는 것인데, 이 경우 특정 스레드의 동작을 커스터마이즈할 수 있게 된다. 둘째로, 자체 ThreadGroup을 정의하고 그룹 내에 생성된 스레드의 uncaughtException() 메소드를 오버라이드하여 해당 스레드의 동작을 변경할 수 있다. 셋째로, Thread의 정적 setDefaultUncaughtExceptionHandler() 메소드를 호출하여 모든 스레드의 기본값 동작을 설정할 수 있다.

ThreadsetUncaughtExceptionHandler()setDefaultUncaughtExceptionHandler() 메소드 모두 UncaughtExceptionHandler 인터페이스 인자의 구현을 수용한다. 이 인터페이스는 Thread 클래스의 내부 인터페이스이므로 정식 명칭은 Thread.UncaughtExceptionHandler이며, 하나의 메소드를 가진다.
   void uncaughtException(Thread t, Throwable e)
uncaughtException 메소드의 구현을 인터페이스의 커스텀 구현의 일부로서 또는 ThreadGroup의 오버라이드된 메소드로서 제공하여 커스터마이즈된 동작을 얻을 수 있다. 예시를 위해, 런타임 예외와 마주칠 때마다 텍스트 영역의 말미에 첨부되는 스택 트레이스가 포함된 창을 표시하는 UncaughtExceptionHandler의 구현을 살펴보기로 하자. 예외 사이에 창을 닫을 수 있으며, 창은 다음 예외가 발생할 때 다른 창 앞에 다시 표시된다
   import java.awt.*;
   import java.io.*;
   import javax.swing.*;

   public class StackWindow extends JFrame
       implements Thread.UncaughtExceptionHandler {

     private JTextArea textArea;

     public StackWindow(
      String title, final int width, final int height) {
       super(title);
       setSize(width, height);
       textArea = new JTextArea();
       JScrollPane pane = new JScrollPane(textArea);
       textArea.setEditable(false);
       getContentPane().add(pane);
     }

     public void uncaughtException(Thread t, Throwable e) {
       addStackInfo(e);
     }

     public void addStackInfo(final Throwable t) {
       EventQueue.invokeLater(new Runnable() {
         public void run() {
           // Bring window to foreground
           setVisible(true);
           toFront();
           // Convert stack dump to string
           StringWriter sw = new StringWriter();
           PrintWriter out = new PrintWriter(sw);
           t.printStackTrace(out);
           // Add string to end of text area
           textArea.append(sw.toString());
        }
     });
   }
  }
핸들러를 테스트하기 위해서는 핸들러를 설치한 다음 런타임 예외를 throw하는 프로그램이 필요한데, 다음의 프로그램 DumpTest가 이 작업을 수행한다. 단순성을 감안하여, DumpTest는 2개의 예외만을 생성한다. 사용자 여러분은 더 난해한 코드를 마음대로 프로그램에 추가하여 더 많은 예외가 throw되도록 한다. 이 때, 프로그램은 예외 사이에서 일시 중지됨으로써 사용자가 예외 사이에 예외 덤프 스택 창을 닫을 수 있음을 보여준다.
   import java.io.*;

   public class DumpTest {
    public static void main(final String args[]) 
     throws Exception {
       Thread.UncaughtExceptionHandler handler =
         new StackWindow("Show Exception Stack", 400, 200);
       Thread.setDefaultUncaughtExceptionHandler(handler);
       new Thread() {
         public void run() {
           System.out.println(1 / 0);
         }
       }.start();
       BufferedReader br =
         new BufferedReader(new InputStreamReader(System.in));
       System.out.print("Press Enter for next exception");
       br.readLine();
       new Thread() {
         public void run() {
           System.out.println(args[0]);
         }
       }.start();
       System.out.print("Press Enter to end");
       br.readLine();
       System.exit(0);
     } 
   }
StackWindowDumpTest를 컴파일한다. DumpTest 실행 시 콘솔에 다음과 같은 내용이 표시되어야 한다.
   > java DumpTest
   Press Enter for next exception
아울러, 텍스트 영역의 예외를 위한 스택 트레이스가 포함된 창이 표시된다.

UncaughtExceptionWindow Window

Enter를 누르면 콘솔에 다음과 같은 내용이 표시되어야 한다.
   Press Enter to end
또한, 창 내의 텍스트 영역에 첨부된 또 다른 스택 트레이스가 표시되어야 한다.

UncaughtExceptionWindow Window

캐치되지 않은 예외를 처리하는 작업은 이것 말고도 더 여러 가지가 있다. Modal Dialogs(여러가지 양식의 대화상자)의 경우에는 자체 이벤트 스레드를 요구하며, 따라서 자체 Uncaught Handler가 필요하다. 시스템 속성 sun.awt.exception.handler는 모든 경우를 커버할 수는 있지만 문서화 수준이 아직 미흡한 상태이다. 한편, 속성을 정식 API에 포함시키기 위해 RFE(Request for Enhancement)가 이미 제출된 상태이다.

예외 처리의 Best Practices 외에도, 2002년 5월의 StackTraceElements에 관한 팁에서도 유용한 참고 정보를 얻을 수 있다. 또한, 자바 튜토리얼에 포함된 레슨 Handling Errors Using Exceptions도 함께 참조할 것.

"Java SE" 카테고리의 다른 글

Posted by 1010