[Java] 비정상종료시 처리 방법 (UncaughtExceptionHandler)
서비스의 특성상 지속적으로 제공해야하는 서비스인데 비정상적으로 종료될 경우, 비정상종료의 원인을 파악할 수 있는 로그를 남기고 서비스를 재실행하여 서비스를 계속 제공하면 된다.
그렇다면, 이런것을 가능하게 하는 방법은 무엇일까? Java 1.5 부터 지원된 기능으로 setDefaultUncaughtExceptionHandler와 setUncaughtExceptionHandler 메소드이다. 메소드 이름을 보면 알겠지만, 예상할 수 없는 예외사항을 처리하는 핸들러를 지정하는 것이다.
static 함수인 setDefaultUncaughtExceptionHandler은 모든 스레드에서 발생하는 uncaught exception을 처리하는 핸들러를 지정할 때 사용하며, setUncaughtExceptionHandler은 해당 스레디에서 발생하는 uncaught exception을 처리하는 핸들러를 지정한다.
예제 코드는 아래와 같다.
package kr.yoongi.java.tutorial; public class TutotialUncaughtExceptionHandler { class Task implements Runnable { @Override public void run() { Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable e) { // TODO 처리 코드 입력 System.out.println(thread + " throws exception: "+ e); // TODO 재실행을 위한 스레드 생성 // new Thread(new Task()).start(); } } throw new RuntimeException(); } } public static void main(String[] args) { Thread t = new Thread(new Task()); /* Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable e) { System.out.println("DefaultUncaughtExceptionHanlder / " + thread + " throws exception: "+ e); } }); */ t.start(); } }
위의 코드를 실행하면 아래와 같이 콘솔에 출력됩니다.
Thread[Thread-0,5,main] throws exception: java.lang.RuntimeException
여기까지는 Java 에서 사용하는 방법을 설명하였다. 만약 Android 에서 사용한다면 아래와 같은 상황을 고려해야 한다.
- 예외 사항을 처리한 후 앱 종료 방법
- 앱 종료의 비정상종료를 나타는 UI를 표현하는 방법
- 앱 종료 후 재실행하는 방법
Android 에서 사용하는 방법은 다시 작성하도록 하겠다.
[참고]
1) https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html,
2) https://developer.android.com/reference/java/lang/Thread.UncaughtExceptionHandler.html
[VirtualBox] 호스트와 게스트간의 파일 공유
윈도우에서 버츄얼 박스를 이용하여 우분투를 설치하고 공유폴더를 설정하여 윈도우(host)와 버츄얼박스에 설치된 우분투(guest) 간의 파일을 공유하려고 했는데, '허가 거부'라는 에러가 발생하여 이를 해결하는 방법을 설명하려고 한다.
먼저, 버츄얼박스에 우분투를 설치하고 공유폴더를 설정하는 방법을 설명하고 에러를 해결하는 방법을 설명한다.
1. 버츄얼박스에 설치된 우분투에 공유폴더 생성
버츄얼박스에 우분투를 설치하고 공유폴더 및 확장(?) 기능을 사용하기 위해서는 '게스트 확장' 프로그램을 우분투(guest)에 설치해야 한다. 설치하는 방법은 매우 간단하다.
버츄얼박스의 우분투를 실행하고 아래와 같이 버츄얼박스 메뉴의 [장치 -> 게스트 확장 CD 이미지 삽입] 를 선택한다.
CD 이미지가 삽입되면 아래와 같은 팝업창이 나온다. 여기서 [실행] 버튼을 클릭한다.
[실행] 버튼을 클릭하면 터미널 창이 나타나고 '인증'이라는 팝업창이 나타나고 관리자 권한이 필요한 동작을 수행하기 위한 관리자 암호 입력을 요청한다. 이 때, 관리자 암호 (또는 최초 생성한 계정의 암호)를 입력하면 설치가 진행된다. 진행이 완료되면 시스템을 재부팅시켜준다.
재부팅 후 버츄얼박스 창의 화면을 변경하였을 때, 우분투 화면이 100% 동일하게 변경되면 '게스트 확장 프로그램'이 정상적으로 설치된 것이다. (설치전 상태를 캡쳐안해서 설명할 방법이 없네요;)
게스트 확장 프로그램이 설치 안되어 있을 때 버츄얼박스 프로그램 창의 크기를 크게하면, 위의 이미지에서 빨강색 부분이 희색(?)으로 보이게 된다. 하지만 게스트 확장 프로그램이 정상적으로 설치되며 회색부분이 없이 꽉찬화면으로 보여지게 된다.
2. 버츄얼박스의 우분투에서 공유폴더 접근하는 방법
먼저 윈도우(host)에 공유할 폴더를 생성합니다. 예를 들어, 'D;\sample\AndroidShare' 라는 폴더를 생성한다.
공유할 폴더를 생성 후 버츄얼박스 메뉴 중 [장치 -> 공유 폴더 -> 공유 폴더 설정]을 선택한다.
설정이라는 팝업 창이 보이고 오른쪽 상단에 아이콘을 선택하면 아래와 같은 설정 화면이 나온다.
'폴더 경로' 는 윈도우(host)의 경로를 지정한다. 예를 들어, 이전에 생성한 'D;\sample\AndroidShare' 폴더를 탐색기를 이용하여 선택한다.
'폴더 이름' 은 우분투(guest)에 표시되는 디렉토리명이다. 예를 들어, 위의 폴더를 지정하면 자동으로 AndroidShare 라는 이름이 입력되는데, 이를 변경해도 된다.
그 외 옵션은 사용자가 알맞게 설정하면 되는데, 마지막 옵션인 '항상 사용하기'를 체크하면 다음 부팅시에도 적용되는 것이고 체크되지 않는다면 이번 한번만 설정하여 사용하는 것이다.
설정이 끝나고 우분투 화면에서 해당 폴더에 접근하려고 하면 아래와 같은 오류 메시지를 확인할 수 있다.
$ cd /media/sf_AndroidShare/
bash: cd: /media/sf_AndroidShare/: 허가 거부
그래서 권한을 확인해보면, 아래와 같이 sf_AndroidShare 디렉토리는 사용자 root, 그룹 vboxsf 로 되어 있고, 현재 사용 계정의 그룹에는 vboxsf 가 설정되어 있지 않아서 접근이 안되는 것을 확인할 수 있다.
$ ls -al /media/
합계 12
drwxr-xr-x 4 root root 4096 7월 8 10:58 .
drwxr-xr-x 25 root root 4096 7월 8 10:58 ..
drwxr-x---+ 2 root root 4096 7월 8 10:58 android
drwxrwx--- 1 root vboxsf 0 7월 8 10:58 .sf_AndroidShare
$ groups
android adm cdrom sudo dip plugdev lpadmin sambashre
$
아래와 같이 현재 사용중인 계정에 vboxsf 그룹을 추가하면 접근할 수 있다.
$ sudo usermod -G vboxsf -a android {현재 사용 계정먕}
[sudo] password for android: {암호}
$ groups
android adm cdrom sudo dip plugdev lpadmin sambashre vboxsf
$ cd /media/sf_AndroidShare/
$ pwd
/media/sf_AndroidShare/
[jquery] 체크박스(checkbox) 전체선택/해제
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
$('#checkedAll').click(function() {
// 만약 checkedAll 이 'checked'되어 있다면
if ($('#checkedAll').prop('checked')) {
// checkbox 타입의 input 중 name이 subCheck 인 것들의 checked의 prop 값을 true로 변경
$('input[name=subCheck]:checkbox').each(function() {
$(this).prop('checked', true);
});
// checkedAll 이 checked 되어 있지 않다면
} else {
// checkbox 타입의 input 중 name이 subCheck 인 것들의 checked의 prop 값을 false로 변경
$('input[name=subCheck]:checkbox').each(function() {
$(this).prop('checked', false);
});
}
})
});
</script>
</head>
<body>
<table>
<thead><tr><td>전체선택: <input type="checkbox" id="checkedAll"></td></tr></thead>
<tbody>
<tr><td><input name="subCheck" type="checkbox" value=0>: 사과</td></tr>
<tr><td><input name="subCheck" type="checkbox" value=1>: 자두</td></tr>
<tr><td><input name="subCheck" type="checkbox" value=2>: 수박</td></tr>
<tr><td><input name="subCheck" type="checkbox" value=4>: 체리</td></tr>
</tbody>
</table>
</body>
</html>