tomcat 을 사용하고 jsp 소스코드를 수정하여 반영 하는 상황이라고 가정.

오래된 시스템 tomcat 5 이하? 라고 가정

2011년 8월 달에 작업된 파일을 2014년 10월에 수정하는 것으로 가정. 

작업을 진행 하려 했으나 실패하여 롤백을 해야하는 상황이라고 가정

운영서버에 바로 반영 하는 것으로 가정


이런 가정이 맞아 떨어지면 무서운 경험을 할 수 있다. 


꼼꼼한 사람들은 작업전에 기존파일을 이쁘게 백업 해 놓는다


test.jsp : 수정일자 2011년 08월 10일

test.jsp : 수정일자 2014년 10월 8일


반영을 하고 테스트를 하는데.. 뭔가 잘안되서 롤백을 하는 상황.

예전 파일을 다시 운영서버에 덮었는데 정상 동작 하지 않는다. 


여기서 집중력을 잃으면 홀로 헤메이다 큰 낭패를 보며 식은땀을 등으로 흘릴 수 있다.

집중력을 있다면  디버깅을 시작한다.

센스가 있다면 원인을 추측하여 소스보기를 통해 빨리 원인을 찾는다.

경험이 있다면 롤백파일을 수정해서 수정일을 최신화 하거나 tomcat의 work 디렉토리에 해당 파일을 확인하고 지운다.


배신자 톰캣은 때로는 아래와 같은 행동을 하는데 버전별로 어떤지는 모른다.

이전날짜 jsp 파일은 컴파일을 안한다.

톰캣 재시작을 아무리 해도. 컴파일을 안한다. 

Posted by 마법수정화살
,

thread 덤프를 획득 했다면 분석을 해보자.


툴을 이용하자. 

SUM JDK :  TDA , visualvm 플러그인으로도 사용 가능 하다.

IBM JDK : TMDA 

jar 하나이고 java -Xmx500m -jar jca455.jar > 이런식으로 실행할 수 있다. 

TMDA :http://pic.dhe.ibm.com/infocenter/isa/v4r1m0/index.jsp?topic=%2Fcom.ibm.esupport.tool.tmda.doc%2Fdocs%2Freadme.htm


thread 덤프를 확인해 보자


전체적인 문제라면 

전체에서 사용하는 객체에서 lock이 있거나 threadpool을 차서 더이상 요청을 처리할 수 없을때. 

특정 동작이 문제라면 

해당 Thread를 찾아 stack trace로 확인한다.


1. 전체 thread 숫자 와 상태 확인 (IBM JDK의 경우 더많은 시스템 정보를 제공한다.)

- 전체 thread가 was에서 허용 가능한 자원을 다 썼다면 해당 웹 어플리케이션은 응답이 없을 것이다.

2. 각 thread의 상태 확인.

NEW: 새로운 쓰레드로 아직 시작되지 않음
RUNNABLE: JVM이 동작중. 이는 항상 동장하고 있다는 것은 아님. 리소스 획득을 위해서 잠시 대기 중일 수 있다.
BLOCKED: 쓰레드가 synchronized block 혹은 method에 진입하기 위해 대기.
WAITING: 대기 상태로 다른 쓰레드가 작업 중임을 의미. 이는 Object.wait 메소드 호출 후에도 진입되는 상태
TIMED_WAITING: 쓰레드가 특정 시간을 대기함을의미. 이는 Thread.sleep(), Object.wait()로 시간 인자가 들어간 메소드가 호출될때 진입되는 상태. 혹은 LockSupport, ParkNanos, LockSupport, ParkUntil 메소드도 동일
TERMINATED: run 메소드에서 빠져나온 경우 또는 예외가 발생하여 빠져나온 경우

데드락이 없는지 BLOCKED나 WAIT를 유심히 확인한다.

thread는 각각 고유한 정보인 THreadID를 가지고 있다. 문제가 있는 Thread 우선 확인해야 한다. 


Thread 덤프는 한 시점에 대한 정보이므로 여러번 수집하여 어떻게 동작하는지 확인해야 하는 경우도 있다.

3. thread의 stack trace를 유심히 보자.  (아래와 같은 상황을 찾을 수 있다.)

- 어떤 메서드를 실행 중인지

- 어떤 메서드에서 대기중인지 (해당 메서드에 동기화 처리가 되어있는지 확인해 본다)

- DB 풀을 기다리는지 

- DB의 응답을 기다리는지 

등등 

Posted by 마법수정화살
,

thread 덤프를 보고 문제를 찾아야 될 때가 되었다면..

난감한 문제 (서버의 응답이 없거나 ,느리거나. 특정 기능이 안되던가.. 등등) 와 만났을 것이다. 


1. 실행중인 java 버전이 1.4 이하라면.. (경험 해 보지 못하고 쓴 부분이 있습니다.)

linux : kill-3 [java process]

AIX 는 정말 잘된다. 다른 unix계열은.. 글세.. 해당 시그널이 가면 stdout에 출력이 되는 경우도 있고 

{java}/bin 밑에, 혹은 홈디렉토리에 생성되는 경우를 본적이 있다.

- windows :  

창모드 -> Ctrl + Break (해당 console에 출력됨.)

서비스 모드 -> SendSignal 을 이용하여 원하는 프로세스에 Signal(Ctrl+Break)을 보낼 수 있다.

(wrapper 로그에 남을듯..)

* java service wrapper의 설정. 아래 글을 보면 아래 설정을 하면 signal을 jvm에게 전달 할 수 있다고 한다.

java webservice의 현재 버전에 대한 내용이므로 참조만..

wrapper.request_thread_dump_on_failed_jvm_exit=TRUE

http://wrapper.tanukisoftware.com/doc/english/prop-request-thread-dump-on-failed-jvm-exit.html

http://wrapper.tanukisoftware.com/doc/english/qna-timers.html#dumps //특정시간마다 thread dump

http://wrapper.tanukisoftware.com/doc/english/prop-filter-x-n.html // 특정 이벤트시 dump 


2. java 버전이 1.5 이상이면. 

1. 위 방법과 동일  

2. threadDump.jsp 로 확인. (jsp를 업로드 하고 호출)

- 1.5에서 제공하는 메서드를 사용 함. 1.4 버전용은 안보임. 간편해서 좋음.

- http://www-01.ibm.com/support/docview.wss?uid=swg21439803

- http://greatkim91.tistory.com/167 : 한글로 잘 설명되어 있습니다.

2. java 1.5 버전 jconsole, java 1.6 버전 jvisualvm

아래 옵션을 JAVA 실행시 옵션에 추가. 

-Dcom.sun.management.jmxremote 

-Dcom.sun.management.jmxremote.port=2020 

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

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

- jconsole 실행후 local에 프로세스가 잡히면 그것을 선택하면 되고 아니면 remote로 port 정보를 추가로 입력하고 JVM을 확인 할수 있다. 기본적으로 thread dump 탭이 보이지 않았는데 플러그인을 설치하면 되는것 같다. 

- jvisualvm 은 visualvm으로 검색하여 다운도 받을 수도 있다. java6 이상이면 기본적으로 {JAVA_HOME}\bin 안에 있다. 

thread dump 파일을 얻을수 있다.

3. jstack 

widnows는 jdk 1.5의 특정버전 이나 jdk 1.6 버전 에서 쓸 수 있다.

- 사용법은 패스 > http://kwon37xi.egloos.com/2871508 

프로세스 아이디 (pid)를 알아내려면 

- UNIX, Linux, OSX : ps -el | grep java

- Windows : Ctrl+Shift+Esc > 보기 > 열 선택 > pid 체크


TOMCAT 팁. : 설치 버전 톰캣을 사용 중이라면, 트레이 아이콘을 우클릭하면  context Menu에 Thread Dump 라는 항목이 있다.

그밖에 키워드로는 javadump.exe 가 있는데 한번 해봐도 잘 안됨..






Posted by 마법수정화살
,

개발시에 많이 겪는 문제 일수 있다. 

멋쟁이 아틀라시안의 정보를 참조함.

https://confluence.atlassian.com/display/STASHKB/Login+and+session+conflicts+with+multiple+Atlassian+applications

증상

같은 어플리케이션을 설정을 달리하여 2개를 구동하였다.

ex) http://localhost:8080 and http://localhost:8090

1번재 서버에서 로그인후 2번째 서버에 로그인을 하면 타이머로 ajax를 통해 서버가 호출 된다면

2군데 페이지에서 세션이 바뀌고 로그인이 팅길수 있다.


이유 

java 웹 어플리케이션은 세션아이디를  브라우저 쿠키에 저장한다.  

이 쿠키는 도메인 과 컨텍스트 별로 관리되지만 port 는 무시한다.

그래서 1번 어플리케이션과 2번 어플리케이션 간의 세션 정보를 덮어 쓸수 있다.


해결방법

1. 도메인을 다르게 하거나 다른 context path로 설정. (귀찮음)

2. 각각 어플리케이션을 서로 다른 브라우저로 사용한다. 1은 크롬 2번은 파폭


[JETTY 에서만 확인해 봄 톰캣에서도 발생한다고 함.]




Posted by 마법수정화살
,

[JAVA] sealing violation

JAVA 2014. 4. 17. 20:25

JAR를 패킹 할때 선택적으로 sealed 여부를 정할 수 있다.

클래스들 사이에 버전 일관성을 보장한다고 하는데 맘에 안든다. 이유는 아래.


http://docs.oracle.com/javase/tutorial/deployment/jar/sealman.html

jar를 까서 manifest 파일을 보면 아래 설정 값이 있다.

Sealed: true


사용할때. 같은 A 클래스를 A.jar에서 로딩하고 다시 B.jar에서 같은 A클래스를 로딩하려고 하면 

sealing violation 이 발생한다. 


대표적인 것이 ojdbc 등 jdbc 드라이버 류 인데 maven-surefire 에서 수행된 후 embed was를 구동해서 테스트 하려고 하면 sealing violation 과 함께 에러 작렬

순서를 바꾸면 먼저하는 놈은 성공한다. 

ANT, application, WAS 에서 쓰려고 하니 난리가 난적이 있다. 


그래서 maven에서  dependency를 아예 제거한 후 Thread.currentThread().getContextClassLoader() 를 저장해 놓고 대충 아래와 같이 jdbc 드라이버를 로딩한다. 다 끝나면 저장되있던 원래 classloader로 세팅하여 깨끗하게 만든다. 

            URL[] urls = { 

                           new java.io.File(OJDBC6).toURI().toURL()};

            URLClassLoader urlCL = new URLClassLoader(urls, contextCL);

            Thread.currentThread().setContextClassLoader(urlCL);

귀찮으면 먼저 로딩된 jar를 나중에 사용하는 것들이 사용하도록 classpath를 적절하게 사용한다.

끝.



Posted by 마법수정화살
,

[JUNIT] 실행순서

JAVA 2014. 4. 17. 18:59

빌드를 기다릴때 가끔 블로그가 생각난다.. 

Junit 의 @Test 메서드의 수행 순서는 정해져 있지않다. 

이번 버전 소스는 봐도 의미가 없으므로 4.11 버전을 보자. 

Junit 4.11 버전 부터는
@FixMethodOrder(MethodSorters.TYPE) 를 이용하여 메서드 수행 순서를 정할 수 있다.

MethodSorters.JVM: the order in which the methods are returned by the JVM, potentially different order each time
MethodSorters.DEFAULT: deterministic ordering based upon the hashCode
MethodSorters.NAME_ASCENDING: order based upon the lexicographic ordering of the names of the tests.

1. MethodSorters.DEFAULT 는 test 대상 class의 getDeclaredMethods() 을 통해 메서드를 얻어온두 

method객체 의 getname 을 hashcode() 한 값으로 sort하여 메서드의 실행 순서를 정한다. 

2. MethodSorters.NAME_ASCENDING 은 말대로 method객체의 getname() 을 이용해 sorting 

3. MethodSorters.JVM 은  getDeclaredMethods() 에서 넘어오는 그대로 전한다. 


문제는 클래스의 모든 method 객체를 가져오는 getDeclaredMethods()  요놈인데 순서가 지멋대로 이다. 

junit을 4.11을 사용하거나 JDK 7 에서 순서가 뒤집히는 경우를 당할 수 있다. 


처음부터 메서드 이름을 순서를 생각 않고 작성했는데 메서드 실행 순서가 중요하고그런 테스트 케이스가 1000개 이상이라면 대략 당황스럽다. 


귀찮아서 일단 검색하다 멋진 아저씨를 발견.

http://intellijava.blogspot.de/2012/05/junit-and-java-7.html


이 아저씨의 아이디어는  getDeclaredMethods()로 가져온 method 를 컴파일된 class에서 byte 코드를 분석하여

linenumber와 index를 알아오고 그걸로 sorting 하는 멋진 방법이다. 


고대로 따라해서 되면 다행이지만 2% 부족한 부분이  linenumber를 가져오는 부분이다. 

저거보다는 ASM 이나 javassist, BCEL 를 이용하면 좋다. 


ASM은 예제부터가 어렵고 코드가 길어서 만만한 javassist로 linenumber를 구해왔다. 

javassist는 powerrmokito에 dependency가 있으니 powerrmokito를 이용한다면 javassist를 쓰자.


이랗게 하고 테스트를 마치고 흡족해 하였으나. 

몇가지 부족한 점이 보였다. 


1. @FixMethodOrder 를 안먹는다. (이부분은 새로운 타입을 추가하면 간단할듯.)

2. SpringJUnit4ClassRunner 수정한 부분을 타지 않는다. 

3. Maven-surefire-plugin useSystemClassLoader false 설정일때 클래스를 찾지못한다. 

   useSystemClassLoader 설정은 2.4 부터인가 기본설정이 true로 바뀌었다. 


나머지는 별문제가 되지않지만 2번문제는... 난감.. 

당황하지 않고  새로운 클래스를 만든뒤 SpringJUnit4ClassRunner를 상속하고 sorting하는 부분만 오버라이드 하고 돌려보니 성공. 

하지만 @TransactionConfiguration 등 스프링에서 제공하는 기능을 쓸때 안되는 문제가 발견 되었다.

걍 맘편히 BlockJUnit4ClassRunner 에 구현하면 해결 될듯하다. 근데 다 하고 나니까 이게 귀찮다.. 









Posted by 마법수정화살
,