구구구구우
1k
2017-12-26 15:36:57
2
1531

윈도우의 명령프롬프트 인수 제한과 ClassPath


https://okky.kr/article/419481

이전에 올렸던 문제에 관한 내용입니다.

해당글에서 언급한 것처럼 이클립스에서 어플리케이션인 실행중임을 알리는 빨간 중지 버튼이 활성화 되었다가 5~10초후 아무런 에러문구 없이 비활성화 되는 현상입니다.(어플리케이션이 종료가 되었다는거죠)

당시에는 정확한 원인을 알지 못했지만, 빌드패스 등록과 관련하여 문제라고만 생각했었습니다.

그런데 이게 자바 실행스크립트(윈도우 .bat파일)를 작성해서 하면 된다는 겁니다.

그래서 이클립스와 연관된 문제라고 생각도 했었죠

그런데 시간이 지나고 몇가지 라이브러리가 추가되면서 클래스패스에 길이가 늘어나니 실행스크립트에서도 되지 않더구요. 대신 실행스크립트에서 실패는 JVM 오류 다이얼로그를 띄웠습니다. 

이때 저는 명령인수에 제한이 있지 않을까라는 생각이 들었습니다. 찾아보니 제한이 있더라구요

그래서 실행스크립트에서 클래스 패스를 작성할때 경로를 최대한 줄여서 작성하였습니다. 

상대경로를 쓰고, .jar파일에 경우 하나의 폴더로 * 를 써서 해당 폴더의 포함된 jar를 모두 클래스패스 등록하는 방법을 썼습니다. (java -cp ./lib/*;./bin application.main.Test)

위의 경우는 소스의 class파일을 jar로 압축하지 않고 사용하기위해 쓴 방법인데

사실 소스의 class파일을 jar로 압축하는 경우는 더욱 간단히 이러한 문제를 해결해줍니다. 사실 Java에서는 이러한 문제를 알고 있었고 이를 위해 제공하는 방법이 있는데 지금 소개할 방법이 그렇습니다.

바로 jar파일에 Manifest파일을 첨부하는거죠.아래와 같은 형태로 클래스 패스를 Manifest를 명시하여 작성하게 되면 아래에 작성된 클래스 패스는 명령프롬프트인수로 들어가지 않기 때문에 이러한 제약에서 벗어날수 있습니다.

사실 명령프롬프트의 인수제한에서 벗어나기 위해 기본적인 가이드를 어디서나 찾아 볼수 있는데 첫째는 상대경로, 둘째는 명령인수를 파일로 옮겨라 입니다. 여기서 Manifest는 파일로 옮겨라라는 규칙을 잘따르고 있죠. 그리고 Manifest파일은 우리가 클래스 패스에 등록한 라이브러리를 로드하는 시스템클래스로더 가 Manifest파일을 처리하게 됩니다.

Manifest-Version: 1.0

Class-Path: lib/xxx1.jar  lib/xxx2.jar  lib/xxx3.jar  lib/xxx4.jar 

Main-Class: application.main.Test

하지만 제가 원하는건 jar압축을 거치지 않은채 class파일 들로만 실행을 원하는 것이었기 때문에(개발중이니까요 배포의 경우 Manifest를 작성하여 배포하고 있었습니다.) 첫번째 실행 스크립트 방법을 사용했습니다. 

그래도 더욱 간단한 방법은 이클립스에서 Ctrl+f11 단축키만 누르면 실행되겠끔 하는것이 제일 좋겠죠. 그래야 디버깅도 이클립스에서 가능할테니까요. 그래서 이클립스에서 빌드패스 등록되는 길이를 줄여보는 노력을 해보았습니다.  (java -cp ./lib/*;./bin application.main.Test) 이런식의 등록 방법이 있는지 찾아보는거죠

그래서 이것저것 해보았는데, 이클립스에서 우리가 빌드패스를 등록하는 작업은 JDT로 하여금 컴파일 관한 정보를 전달하는 것입니다. 이러한 정보를 바탕으로 우리는 Run Configuration을 작성하게 되는데 보통 main메소드 클래스에서 Ctrl+f11 단축키만 누르면 우리가 등록한 빌드패스를 포함하는 .launch파일이 작성이 되죠 이것을 가지고 이클립스의 실행 플러그인 동작을 하게되구요

그럼 이제 부터 그림을 좀 보도록 하죠


클래스패스 탭을보면 우리가 실행할 어플리케이션에 클래스 패스가 보일겁니다. 상단에 적혀있는 부트스트랩 엔트리가 눈에 띄네요. 자바 어플리케이션이 실행될때 가장먼저 부트스트랩클래스 로더가 클래스들을 로드합니다. 대표적으로 rt.jar 가 있습니다. 그리고 확장클래스로더가 ext폴더에 있는 클래스들을 로드하구요 마지막으로 시스템클래스로더가 우리가 클래스 패스에 작성하는 클래스들을 로드합니다. (사설이 길었네요...)

어쩃든 유저 엔트리에 있는 .jar파일들이 우리가 등록한 클래스들입니다. 그리고 Test프로젝트에 Test도 등록 되어 있네요. Test프로젝트에 작성한 소스의 클래스 파일들이 bin 폴더에 있으니까 그부분도 클래스 패스에 등록 하여야 되기 때문이겠죠. 이게 우리가 빌드패스를 등록하고 단축키만 누르면 알아서 이렇게 장성이 됩니다. 그리고 해당 내용은 프로젝트의 워크스페이스에workspce\.metadata\.plugins\org.eclipse.debug.core\.launches 경로에 .launch파일로 저장이 되고 이클립스에 실행 플러그인은 이 파일만 가지고 실행을 합니다. (참고로 .launch파일들이 간혹 잘못 관리되어 지워지 않은채로 남아 있는 경우도 있는데, 그냥 지우시면 되요, 또 이 launch파일 들을 토대로 runnable.jar나 Manifest를 작성하는데 필요없는 launch파일들이 있으면 상당히 귀찮습니다. )

그럼 다시 설명을 이어서 우리가 하려는 (java -cp ./lib/*;./bin application.main.Test) 이런식 의 등록방법 즉 jar하나 하나 등록이 아닌 jar파일을 포함하는 폴더를 등록하여 한번에 jar를 등록 하는 방법에 대한 소개입니다. 보시면 advance라는 버튼이 있는데 거기에 보시면 다양한 라디오 선택 버튼이 있는데 적절하게 하나 고르시면 됩니다. 지금은 폴더겠죠(제경우는 Add Varialble String도 많이 씁니다.)

참고로 자바에서 제공하는 폴더의 하위 Jar를 모두 로드하는 방식은 재귀 형식은 안됩니다. 

lib/* -> lib에 하위 jar파일만 로드하지 그밑에 폴더안에 jar까지는 로드하지 않아요 

이것말고도 시스템 클래스 로더를 새롭게 구현하는 방법도 소개를 받았었습니다. 자바의 시스템 클래스 로더가 Manifest를 읽어서 처리하는것처럼 우리도 .launch파일에 적힌 앤트리 정보를 로드해서 실행하겠끔 하는 시스템 클래스로더 새로이 작성하는거죠. 이부분도 한번해보세요 찾아보셔서 해보시면 꽤 재미있스빈다.


다시 처음에 내용으로 돌아가서 말씀을 드리면 이문제에 대한 원인은 정확하게 파악하지 못했습니다. 왜냐하면 해당 문제는 다른 컴퓨터에서는 발생하지 않았고 또한 재가 등록한 ClassPath는 윈도우에서 제한한 명령인수의 크기를 넘지도 않았기 때문입니다. 또한 JavaFX 어플리케이션이 구동이 될때 하드웨어 랜더링 모드가 기본 default로 사용이 되는데, 이것을 소프트웨어 랜더링 모드로 실행하면 같은 사이즈의 클래스 패스 일지라도 하드웨어 랜더링 모드에서는 안되고 소프트웨어 랜더링 에서는 실행이 됩니다. 즉 단순히 명령프롬프트 인수 제한 문제만은 아니라는거죠, 무언가 복합적인 문제인겁니다.

 

하드웨어 랜더링 모드에서 verbose를 확인하면 로드되는 클래스와 DLL을 확인할수 있는데 윈도우의 경우 prism_d3d.dll를 로드합니다. 소프트웨어 랜더링 모드에서는 로드하지 않구요.

어쩃든 정확한 원인을 파악하지 못한채 오늘 실행을 해보니 해당 문제가 사라졌습니다. ........

뭐가 문제였던건지 모르겠지만, 그동안 했던 작업이라고는 윈도우 업데이트, QT 어플리케이션 작성 .... 등등 짐작할수 없는것 뿐이네요









0
  • 답변 2

  • Karen
    15k
    2017-12-26 18:43:40

    구구구구우 // 안녕하세요~ 해당 글은 사는얘기보다는 Q&A나 포럼이 맞는 것 같아 일단 Q&A로 옮겨드렸습니다 :) 혹시 포럼이 더 알맞다면 옮겨주세요~~

  • 구구구구우
    1k
    2017-12-26 22:40:40

    네 그런데  저도 어디가 좋을지는 잘 모르겠네요.ㅎㅎ

  • 로그인을 하시면 답변을 등록할 수 있습니다.