기본 콘텐츠로 건너뛰기

[ JAVA ] JVM Shutdown 상황에서도 로그를 출력해 보자.

  현재 진행 중인 프로젝트는 Java Agent 를 이용해서 JVM 상에 구동되는 Application의 성능을 측정하기 위한 Custom Profiler를 구성하는 것이기에 Logging 정보가 상당히 중요하고, JVM Shutdown 되는 시점을 Hooking 하여 Agent 의 서비스들이 제대로 종료하는지를 검증하여야 한다.   그런데 JVM Shutdown Hooking 을 하게 되면 그 시점에 java.util.logging.LogManager 를 사용하고 있다면 Logger 들이 모두 Clear 되어 버리기 때문에 완전히 종료되는 시점까지 Logging 을 처리할 수 없다. *** 지금부터 언급하는 상황들은 java.util.logging.LogManager를 사용하는 경우에 해당하는 것이므로 다른 logging 처리를 하는 경우에는 해당되지 않는다. 다만 처리되는 상황이 궁금한 경우는 참고가 될 수도 있다. 왜? Logger 가 모두 Clear 되는 것일까?   가장 먼저 문제가 되는 LogManager를 역 추적해 보도록 하자. 우선 어플리케이션에서 JVM Shutdown 을 Hooking 하는 방법은 아래와 같다. // JVM Shutdown Hook Thread를 설정한다. Thread shutdownThread = new Thread(new Runnable() { @Override public void run() { try { LOGGER.info("JVM Shutdown called"); shutdown(); LOGGER.info("JVM Shutdown complete"); } catch (Exception e) { LOGGER.log(Level.SEVERE, "JVM Shutdown completed with errors", e);

[ TOMCAT ] 웹 어플리케이션 수동 배포 해 보기. (간략)

  개발하는 동안에 보통 Eclipse에 Servers 에 Tomcat 을 등록하고 Maven 을 이용해서 배포를 하고 사용하게 된다. 그러나 상황에 따라서는 Eclipse 를 이요하지 않고 Tomcat 에 직접 배포를 해서 테스트를 해야하는 상황이 발생하게 된다. 이 글을 작성하게 된 것도 Eclipse에서 Tomcat 를 실행하면 아무 문제 없는데 테스트 서버에 배포가 되어 실행이 되면 Tomcat 실행 중에 오류가 발생하는 것 때문에 임시로 간략하게 테스트 한 것을 정리해 놓도록 한다.   Tomcat 은 설치 버전이 아니라 Zip 배포 버전을 사용하였고, 위치는 D:\Tomcat7 이며, 웹 어플리케이션은 test-apps (프로젝트 명)라고 가정하고 테스트를 진행하도록 한다. (단, 하기의 내용은 실제 각 개발자의 환경에 따라서 다를 수 있으므로 반드시 맞는 방법이라고 할 수는 없다) 배포 대상 생성 및 복사   Eclipse에서 빌드를 하여 War 파일과 배포 폴더를 생성한다. 보통은 Eclipse에서 Maven 을 이용하여 빌드를 하게 되므로 빌드 후에 해당 프로젝트의 Target 폴더를 확인하면 War 파일과 배포 폴더가 생성된 것을 확인할 수 있다. 배포는 War 파일이 아니라 배포 폴더 (test-apps) 를 이용하도록 한다.   배포 폴더를 복사하여 Tomcat 폴더의 "webapps" 밑에 넣도록 한다. ( 보통은 WebContent / Library / Classes 들을 배포하지만, Maven 빌드릉 통해서 해당 항목들이 모두 배포 폴더와 War에 포함되어 있으므로 폴더채로 복사를 하면 된다) Context 설정 (server.xml 파일)   Tomcat 폴더 밑의 conf 폴더에 server.xml  파일이 존재한다. 이 파일에 Tomcat 이 구동되었을 때 사용할 어플리케이션 Context 를 설정해 주어야 한다. server.xml  파일을 열고 "<Host>...<

[ 일반 ] Chrome 에서 Manual Proxy Server 설정하는 방법

   현재 진행 중인 프로젝트 (APM Prototype - Custom Profiler) 에서 부하 테스트를 하기 위해 JMeter를 사용하려고 설정을 하고 있다. 그런데 JMeter에서 시나리오를 생성해서 설정하는 것은 상당히 어렵다. 그래서 제공되는 것이 Http Recording Controller 이다.    간략하게 처리 방법을 정리하면 Thread Group 에 Recording Controller 를 추가하고 WorkBench 에 Http(s) Test Script Recorder 를 추가해 놓고 로컬 Proxy Server 를 통해서 사용자가 브라우저를 통해서 접근하는 모든 Request 를 잡아내어 저장하는 방식이다. (이 부분에 대해서는 테스트 종료 후에 JMeter 사용법을 정리해서 올릴 예정이므로 여기서는 생략하도록 한다)   그런데 문제는 주로 사용하는 브라우저가 Chrome 인데 Chrome 의 설정을 통해서 Proxy Server 설정을 하게 되면 아래의 그림과 같이  Internet Options 창의 Lan 설정창이 열리게 되어 변경하면 외부 연결에 대해서 Proxy Server 가 설정된다는 점이다.   JMeter에서는 Firefox 를 사용하도록 권고 하고 있다. Firefox 에서는 Option > Advanced > Network 설정을 통해서 시스템 전역과는 상관없이 Manual 로 Proxy Server 설정이 가능하기 때문이라고 설명하고 있다.   그렇다고 여러 브라우저를 설치해서 사용하는 것이 싫어서 Chrome 을 이용해서 Manual Proxy Server 를 설정하는 방법을 검색해 본 결과 Chrome 인스턴스에서 설정을 통해서 처리하는 방법은 없고 실행 옵션을 줘서 설정이 가능하기는 하다. "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --proxy-server="서버IP:POR

[ JAVA ] @interface? Annotation??

  Annotation 이란 사전적인 의미대로 주석을 작성하는 것이고, 프로그램적으로 사용할 수 있도록 몇 가지 규칙을 적용한 것이다. 제일 흔한 활용 방식은 부가적인 정보를 제공하기 위한 것이다.   예를 들어 웹 어플리케이션에서 Controller 에 대한 설정을 하려면 해당 규칙 (Spring Framework 등을 사용한다면 그에 맞도록) 을 xml 설정 파일에 정의를 하고 프레임워크가 구동되면 그 정보를 로드해서 운영되는 것이다. 그러나 Annotation 방식을 사용하게 되면 xml 설정 파일에는 컴포넌트 스캔 정보와 같은 단순한 정보를 설정하고 나머지 부가 정보들은 해당 클래스에 Annotation으로 설정을 해 놓으면 같은 동작을 하게 된다. 좀 더 관리 포인트가 줄어들고 해당 클래스의 Annotation을 보면 어떤 역할을 담당하는지를 알 수 있기 때문에 많이 활용된다.   실제 구성된 코드에서 보면 "@" 가 붙은 것들을 볼 수 있다. "@" 는 Annotation을 의미하는 것이고, 가장 많이 볼 수 있는 것이 "@Override", "@SupressWarnings", "@Deprecated", "@Controller", "@RequestMapping", ... 등이다.   Annotation은 다음과 같이 정의를 해서 직접 만들어서 사용할 수도 있다.   @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Simple { public String defaultVal() default "OK"; public String val(); }   위의 코드는 단순한 기능을 제공하는 Annotation을 정의한 것으로 Annotation 의 값을 "val" 이라는 항목

[ JAVA ] Decompile 된 코드에서 this.val$xxx 는 어떻게 해석해야 할까?

  좋은 방법도 아니고 하지 않는 것이 좋기는 하지만 제대로 된 정보를 구할 수 없는 경우에 어쩔 수 없이 외부 jar 파일에서 원하는 기능과 유사한 기능을 구현한  코드를 검토해 봐야할 수 밖에는 없는 경우가 존재한다.  이럴 때는 Decompiler를 사용하여 원본에 근사치의 코드를 얻을 수 있다. Eclipse 에 Decompiler 를  설정하는 부분은 이전 게시글 에 서 설명을 했었다.    이 때 Decompile 된 코드를 검토하다 보면 허걱스런 코드들이 존재하는 것을 볼 수 있다. 여러 가지 상황들이 있지만 많이 만나게 되는 "this.val$xxx" 코드가  한 가지 예라고 볼 수 있다. 이런 경우는 어떻게 해석을 해야할까?   자바 스펙 ( http://docs.oracle.com/javase/specs/jls/se5.0/html/lexical.html#3.8) 에 의하면 '$' 는 변수명으로 사용할 수 있는 문자이지만  다음과 같은 추가 설명이 존재한다. The $ character should be used only in mechanically generated source code or, rarely, to access preexisting names on legacy systems.   위의 문장을 따르면 기계적으로 생성된 소스 코드 또는 극히 일부 기존 시스템의 이미 존재하는 이름을 참조하기 위해서만 사용하여야 한다는 제한적인 의미가 된다. 왜? 이런 제한이 있어야 하는 것일까? (정확한 것이라고 할 수는 없지만 아래와 같이 이해할수 있을것 같다) 내부적인 클래스가 존재하는 것과 같이 인식 되는 경우 - 익명 클래스등과 같은 내부에 Inner Class가 존재하는 경우에 외부 클래스와 내부 클래스를 식별하기 위한 방식으로 outerclass$innerclass 로 사용 된다. 이와 같은 이유로 인해서 메서드의 내부에 익명 클래스가 사용되는 경우도 역시 마찬가지로 적용된다. 난

[ 일반 ] 정규식 (Regular Expression)에 대한 간단 정리

유독 머리에 제 자리를 잡지 못하는 것들 중에 하나인 정규 표현식 (Reqular Expression) 을 또 써야할 상황이 발생해서 다시 한번 정리를 해 놓는다. (언젠가 또 멍청하게 까먹고 다시 정리한다고 하겠지만.. 쩝) 확장자 검증하기 (샘플) 우선  사용하려고 하는 실제 사례에 대한 의미를 확인해 보도록 하자.  실제 적용하여야 하는 부분은 특정한 문자열 (URL, File Path 등...) 에서 이미지 확장자를 제거하는 부분이다. 쉽게 사용할 수 있는 정규식은 다음과 같이 적용이 가능하다. ( [ ^ \s ] + ( \. ( ? i ) ( jpg | png | gif | bmp ) ) $ ) 위의 정규식을 이해하기 위해서 하나씩 끌어내 보면 다음과 같이 해석할 수 있다. ( # 그룹 1의 시작 [ ^ \s ] + # 공백을 제외한 하나 또는 그 이상의 문자열이 존재하여야 한다는 조건 ( # 그룹 2의 시작 \. # '.' 문자 ( ? i ) # 이후의 문자들에 대해서 대/소문자를 무시 ( # 그룹 3의 시작 jpg # 'jpg' 문자 포함 | # 또는 png # 'png' 문자 포함 | # 또는 gif # 'gif' 문자 포함 | # 또는 bmp # 'bmp' 문자 포함 ) # 그룹 3의 종료 ) # 그룹 2의 종료 $ # 문자열 종료 ) # 그룹 1의 종료 위의 내

[ APM ] APDEX 가 뭘까?

APM (Application Performance Mangement) 과 관련해서 나오는 용어들 중에  APDEX라는 것이 있다. 보통 서비스 품질이라는 것은 IT 서비스를 제공하는 IT 부문과 IT 서비스를 이용하는 사용자 사이에 반응 시간 (Response Time)  등의 수치적인 지표를 이용해서 서로 수용 가능한 조건에서 이해하고 이용하는 기준으로 삼는 것이 필요하다. ITIL (IT Infrastructure Library) 에서는 이런 기준이 될 수 있는 지표들을 IT 부문이 관리하는 IT  기반의 기술적인 관점에서 측정 및 관리하려고 하는 반면에 "업무나 사용자 관점에서도 이런 지표들이 필요하지 않을까?" 하는 사고방식에 근거해서 나오는 서비스 관리를 APM이라고 할 수 있고, 이 APM을 실시하는 방법으로 APDEX (Application Performance inDEX) 라는 지표를 이용해서 "사용자 시점의 IT 서비스 관리가 ITIL 과 같이 표준 프로세스화 할 수 있는 것이 아닐까?" 라고 생각하고 이것을 추진하는 사람들이 모인 Apdex Alliance 라는 그룹이 미국에 등장했다. 업무나 사용자 관점에서 IT 서비스 관리를 비즈니스 서비스 매니지먼트 (BSM  - Business Service Management) 이라고 표현하고 이를 위해서는  ITIL 과점의 기술적인 서비스 매니지먼트 (TSM - Technical Service Management) 에 융합시켜 가는 것이 필요하다고 소개하고 있다. 그럼 Apdex Alliance 가 주창하고 있는 Apdex 지표는 무엇일까? Apdex 란? Apdex는 제공되는 IT 서비스에 대해 사용자의 만족도를 수치적인 지표로 잡을 수 있도록 하기 위한 것으로 측정된 데이터에 대해서 0 (불만족) ~ 1 (만족) 사이의 만족도를 표현하기 위한 것이다. 이 지표는 사용자가 실행하는 업무 (Task 라고 표현) 에서 측정된 각 반응 속도 (R

[ JAVA ] Eclipse 와 Tomcat 연동 Port 사용중이라는 오류 (특정상황 피해가기)

웹 어플리케이션을 개발할 때 Eclipse 에서 Servers 로 Tomcat을 등록해서 Web Application을 구동시키는 것이 일반적인 방법이다. 그러나 어떤 경우에는 지정한 포트가 이미 사용중이라는 오류를 만나게 될 수 있다. 이 상황을 구글에서 찾아보면 대부분의 해결 방법이 아래와 같이 제시된다. "사용중인 포트를 확인해서 사용 중인 다른 어플리케이션을 죽여라" 또는 "다른 포트로 변경해서 사용하라" C:\> netstat -ano | find "8080" C:\> taskkill /f /pid:... 일반적인 상황에서는 맞는 말이다. 그러나 제목에 언급한 것과 같이 "특정상황" 이 발생한 상황에서는 이런 해법은 전혀 도움이 되지 않는다. 여기서 말하는 특정 상황이라는 것은 Eclipse에서 Server 탭의 "Start" 버튼을 눌러서 실행을 하게 되면 다른 어플리케이션에서 사용하지 않는 포트임에도 불구하고 이미 사용중인 포트라는 오류가 발생한다는 것이다. 포트를 바꾸면 바꾼 포트가 사용중이라는 오류가 발생하게 된다. 위의 상황을 유추하면 Eclipse 가 Tomcat 구동 명령을 전달할 때 뭔가 문제가 있을 것 같다는 생각으로 해결법을 찾기 시작했지만 구글 검색을 통해서도 별다른 해결 방법을 찾지 못하고 있었다. 이런 상황에서는 대략 아래와 같은 특징들이 발생한다. 포트를 바꿔도 바뀐 포트로 사용중이라는 오류가 발생한다. Debug 모드로 실행하면 제대로 실행되는 경우가 있다. (그러나 Start 로 실행하면 오류가 발생한다) 처음 시스템을 부팅하고 JDK를 재 설치를 한 후에 Eclipse를 실행하면 제대로 실행된다. 단, 재 부팅하고 나면 다시 오류가 발생한다. (이 상황은 이해가 되지 않는다 ㅠㅠ) 더 많은 상황들이 존재하지만 딱히 연관되어 있다고 볼 수는 없기 때문에 생략하도록 한다. 위의 특징들 때문에

[ JAVA ] JDK 버전과 컴파일된 .class 파일의 버전 연관성

ByteCode 로 Instrumenation을 하던 중에 컴파일된 .class 파일을 통해서 사용된 JDK 의 버전을 찾아야 하는 상황이 발생했다. 여러 가지 방법을 찾던 중에 알게된 내용이 있어서 정리해 놓는다. .class 파일의 버전을 얻는 방법은 다음과 같다. > Unix/Linux 환경인 경우 javap -verbose MyClass | grep "major" > Windows 환경인 경우 javap -verbose MyClass | findstr "major" 위의 명령은 .class 에 설정되어 있는 메타 정보들 중에서 "major" 버전 정보를 읽은 것이다. 이 명령의 결과로 다음과 같이 정리할 수 있다. Java 1.2 를 사용한 경우에 Major 버전은 46 Java 1.3 을 사용한 경우에 Major 버전은 47 Java 1.4 를 사용한 경우에 Major 버전은 48 Java 5 를 사용한 경우에 Major 버전은 49 Java 6 을 사용한 경우에 Major 버전은 50 Java 7 을 사용한 경우에 Major 버전은 51 Java 8 을 사용한 경우에 Major 버전은 52 이 버전에 따라서 ClassWriter의 Verify 방법이 달라지게 된다. 따라서 Java 7 이전에 컴파일된 jar를 포함하여 사용하는 경우에 ByteCode 로 Instrumentation을 하는 경우라면 여러 가지 오류들 (Expand_Frames 옵션이 필요하다거나 ClassNotFoundException 등) 피하기 위해서 동적으로 운영할 수 있어야 한다. ByteCode Instrumentation (Using ASM) 을 할 경우에 해당 버전 정보를 대상 클래스에서 찾기 위해서는 아래의 명령을 사용하면 된다. ClassReader cr = new ClassReader(classFileBuffer); int majorVersion = cr.rea

[ Javascript ] == 와 === 차이점.

현재 backbone, requirejs 를 사용하여 프로젝트를 진행 중이다. 클라이언트 사이드의 MVC 구현이다 보니 거의 대부분의 코드가 javascript 다. 스크립트 코드를 작성하면서 이전부터 궁금했던 것들을 하나씩 정리해 보도록 한다. 우선 첫번째로 == (Equality) 와 === (Identity) 의 차이를 이해해 보도록 한다. == 연산자는 left/right 가 다른 형식이면 형식을 강제로 변환하여 값이 같은지를 비교 한다. 그리고 값을 변환하여 비교하는 규칙은 복잡하고 외우기도 쉽지 않다. 반면에 === 연산자는 변환이 없이 있는 그대로를 비교하게 된다. 따라서 변환이라는 부하(?)를 주지 않기 위해서 단순한 비교라면 === 를 사용하는 것이 효과적이다. 다만 무분별하게 변수등을 선언해 놓고 비교한다면 오히려 헷갈릴수도 있으므로 조심해야 할 듯. 간략한 예제를 통해서 내용을 이해해 보도록 하자. var i = 0; console.log(i == '0');     >> true console.log(i === '0')    >> false 위의 코드를 보면 서로 다른 결과를 보여준다. 왜 이런 결과가 나오는지를 이해하면 된다. 변수 i 를 선언하고 0 을 배정했기 때문에 형식은 number 가 된다.  그런데 비교는 '0' 인 string 과 하고 있다. 따라서 == 연산에서는 string을 number로 변환한 후에 비교를 하게 된다. 그러니 값은 서로 같으므로 true 가 된다. 그러나 ===  연산은 값 뿐만 아니라 형식도 모두 비교하는 것이 때문에 (변환 과정이 없다) 값을 비교하기 이전에 형식이 다르므로 false 가 된다. '' == '0' 0 == '' 0 == '0' false == 'false' false == '0