jar 파일은 자바에서 제공되는 압축파일이다. 그러나 단순한 압축 파일이 아닌 라이브러리 처럼 사용되기하고 실행파일 처럼 사용되기도 한다. 그리고 RMI에서는 다른 용도로 사용되기도한다. 이처럼 jar파일은 자바에 있어서 아주 활용도가 높다.
그럼 자신이 만든 프로젝트를 jar 파일로 만들어보자.
여기서 다음과 같은 프로젝트를 사용하기로 하자.
패키지는 logging이고 그 안에 LogServer 클래스가 있고, 이 클래스는 log4j를 사용한다고 가정하자.
(http://ospace.tistory.com/(ospace114@empal.com)) 2008.07.18
Basic
가장 단순한 jar파일을 만들고 압축 및 해제하는 방법을 간단히 보도록 하죠.
다음 두가지 파일이 있다고 하자.
bin\\logging\\LogServer.class
bin\\env.property
현재 bin 디렉토리 위치에 있다.
압축
jar cvf LogServer.jar env.property logging\
옵션 "c"가 압축 옵션이고, "v"가 작업 상황을 출력하는 옵션이며, "f"는 압축되는 파일 이름을 의미한다. "v"는 없어도 화면에 출력이 되지 않을뿐 사용하는데는 지장이 없다. "logging\"라고 디렉토리라고 했는데 다음과 같이 가능하다.
ex)
logging\LogServer.class
logging\*.class
지나가는 글
앞에서 bin디렉토리 안으로 이동한 이유는 자바에서 패키지는 디렉토리 단위로 구성이 된다. 다음과 같이 압축을 하면 bin디렉토리까지 포함되어서 제대로된 경로를 찾기 힘들어진다.
ex)
jar cvf LogServer.jar bin\\env.property bin\\logging\\
그래서 bin 디렉토리로 먼저 이동한 후에 압축을 하였다.
보기
jar tf LogServer.jar
"t"는 압축 파일에 들어있는 목록을 말하며, "f"는 압축파일을 입력받겠다는 의미이다.
지나가는 글
옵션 "f"가 왜 필요한지 의문이 들 것이다. 도스(DOS)창에서 데이터를 입력 받고 출력 받는 방법은 다양하다. 출력은 유일하게 파일만 있는 것이 아니기 때문이다. 많이 사용하는 것 중에 하나가 파이프(pipe)라는게 있다. 보통 기호로 "|"형태로 표시하는데 이는 프로그램 간에 데이터 이동 통로라고 생각하면 된다. 혹은 리다이렉트(redirect)라고 ">"라는 기호를 사용하기도 한다. 이 리다이렉트는 출력을 의미한다고 생각하면 된다. 파일로 출력, 프린트로 출력 등. 그러나 도스에서는 이렇게 까지는 사용하지 않고 유닉스 계열에서 많이 사용한다.
참고로 재미나마 다음과 같이 사용할 수 있다.
ex)
jar cv env.property logging\\ > test.jar
type test.jar | jar t
풀기
먼저 LogServer.jar를 tmp등 임시 폴더로 복사한다.
jar xvf LogServer.jar
옵션 "x"는 압축을 풀겠다는 의미이고 "v"는 작업 과정을 표시하는 것이고, "f"는 압축을 풀 파일을 지정하겠다는 의미이다. 실제 압축을 푼 내용을 보면 내가 추가하지 않은 파일도 보인다. META-INF 디렉토리에 있는 MANIFEST.MF파일이다. 이는 jar가 자동으로 생성되며 단순히 현재 버전 정보만 들어가 있다. 이들의 특별한 역활은 아래에서 보도록 하겠다.
이렇게 해서 대충 jar파일을 압축하고 푸는 것을 보았다. 실제 jar에는 다른 여러 옵션들이 있다. 이는 아래에 친철히(?) 표시해주었다.
기타
이외에 압축을 새로 만들지 않고 파일만 추가하거나 변경도 가능하다. 이때 사용하는 옵션이 "u"이다. 그리고 압축 없이 저장용으로만 사용할수도 있다. 보통 저장용으로는 실행하는데 빠르며, 압축용은 통신으로 전송할때 빠르다. 본인이 직접 확인해보고 테스트하고 확인해보길 바란다.
이렇게 생성된 jar파일을 클래스 패스에 지정해서 jar파일 안에 있는 클래스를 사용할 수 있게 된다. 자신만의 다양한 라이브러리를 만들어서 사용할 수 있다.
Advanced
앞에서 건너뛰 MANIFEST.MF파일에 대해서 더 자세히 보도록 하겠다.
기본정보설정
두가지 항목이 보인다. Manifest-Version과 Created-By이다. 전자는 현재 파일에 대한 버전 정보를 의미하고 후자는 생성에 대한 현재 프로젝트 버전이나 생성 일자에 대한 정보를 말한다. 내용을 보면 일정한 형식이 있을을 확인 할 수 있다. 즉, 가운데 콜론(":")을 두고 헤더와 값이 있다.
형식-> 헤더: 값
헤더와 콜론 사이에는 공배는 없다.
그럼, 입맞에 맞게 manifest.mf파일을 작성해보자. 본인은 다음과 같이 작성하였다. Manifest-Version는 그대로 유지하고 Created-By만 수정하였다.
그럼 jar 압축시 작성한 manifest.mf로 사용하는 것을 보겠다. 당연히 디렉토리는 bin으로 이동한다.
jar cvfm LogServer.jar manifest.mf env.property logging\
앞에서 압축하는 방법과 같지만 추가된 옵션 "m"이 있다. 이는 사용자가 작성한 manifest.mf파일을 사용하겠다는 의미이다. 여기서는 manifest.mf라는 이름을 사용했지만, 본인이 편한 이름으로 사용할 수 있다. 저는 jar에 있는 파일명과 일치시켰서 사용할뿐이다.
실제 압축을 풀어서 내용을 확인해보자.
클래스패스
그럼 조금 속도로를 내보자. 이번에는 jar파일 안에 jar파일을 추가하는 경우를 보자. 일단 설명을 하자면, jar파일안에 jar파일을 추가해서 압축을 하면 보통 추가된 jar파일은 인식을 하지 못한다. CLASSPATH도 통하지 않는다.(이건 확인안해봐서 잘 모르겠다. ㅡ.ㅡ;)
이때 필요한것이 manifest.mf파일 안에 "Class-Path:"라는 헤더이다. 단순히 manifest.mf를 작성해서 다음과 같이 사용하면 된다.
"중요한 건 Class-Path의 위치이다."
중요! 그렇지 않으면 Class-Path가 제대로 적용이 안된다. 추가되는 jar파일 간에는 공배만 추가하면된다. 쉼표(",")나 세미콜론(";")등은 사용할 수 없다.
디렉토리 위치는 LogSever 프로젝트 루트로 이동하다 사용하는 LogServer.jar파일은 루트로 복사하고, log4j-1.2.15.jar는 lib디렉토리에 복사해둔다. 그리고 manifest.mf로 작성해주고 env.propery도 이동해둔다.
...\\Workspace\\LogServer\\LogServer.jar
...\\Workspace\\LogServer\\lib\\log4j-1.2.15.jar
...\\Workspace\\LogServer\\manifest.mf
...\\Workspace\\LogServer\\env.property
LogServer.jar로 새로 작성한다. env.property가 빠졌다.
jar cvf LogServer.jar logging\
Jar파일을 포함한 최종 파일은 LogPack.jar파일이다.
jar cvfm LogPack.jar manifest.mf env.property LogServer.jar lib\lib4j-1.2.15.jar
실제 실행은 아래와 같으니 참고하길 바란다.
jar실행
이번에는 Jar파일을 실행해보자. 당연히 압축된 jar파일 안에는 main 함수가 들어간 클래스가 있어야만 한다. 없다면 백날해도 실행안된다. 절대 안된다.
앞에 예제에서 이런 클래스가 LogServer이다. 압에서 jar파일 안에 jar파일이 존재할 수 있다고 했다. 그 안에 있는 jar 파일에 LogServer같은 클래스가 있어도 된다. 단지 manifest.mf 파인 안에 있는 클래스패스 항목에 해당 jar파일을 지정해주면 된다. 이는 앞에 내용을 참조하길 바란다.
이것 역시 manifest.mf 파일 안에 넣어주면 된다. 입력되는 형식은 아래와 같다.
Main-Class: 패키지이름/클래스이름
LogServer 클래스가 속하는 풀 패키지이름을 넣어주면 된다. 즉, "logging/LogServer"이다.
아래 그림이 manifest.mf에 추가된 내용을 보여주고 있다. 이것 역시 위치에 주의해야 한다. 맨 밑에 추가해서 삽입을 하니 해당 내용이 제대로 들어가지 않은다. 만약에 manifest.mf에 내용을 입력했는데 해당 내용이 jar파일에 적용되지 않는다면, 위치를 변경하길 바란다. 왜 그런건지 이유는 모르겠다.
다시 패키징해서 실행하는 과정은 다음과 같다.
현재는 간단한 예제만 보았지만, 이렇게 하면 복잡한 응용프로그램도 배포버전으로 배포할 수 있겠다.
지나가는 글
앞의 버전은 기존 라이브러리까지 포함해서 패키징 했다. 이렇게 되었을 경우는 프로그램 일부 변경시 전체 프로그램을 다시 패키징해서 배포해줘야하는 문제점이 있다. 작은 프로그램인 경우는 파일 하나만 있기에 간단히 이동, 복사가 가능하는 간편함이 있다. 그러나 커다란 프로그램인 경우는 이렇게 하기 힘들다. 특히 수정후 배포가 가장 힘들다. 이런 경우는 거의 고정된 jar파일들을 하나로 묶고 수시로 변경되는 파일들은 다른 jar파일로 패키징해서 관리하면 좋다.
즉, 기존 프래임워크에 사용하는 라이브러리 등은 고정된 jar파일에 넣어두고, 현재 새로 만드는 소스만 따로 jar파일로 묶어서 배포하는 것이다. 물론 크기에 따라서 분류를 다르게해야되지만...
실링
마지막 실링(Sealing)을 보겠다. 그다지 많이 사용하지 않지만 일단은 알아두면 사용할데가 있을 것 같아서 넣어두었다. 실링(Sealing)은 지정된 패키지에 모든 클래스는 현재 압축되는 jar파일에 존재해야 한다. 만약 다른 jar파일로 존재하는 경우 사용할 수 없다.
다시 말해서 내가 LogServer 클래스를 가지는 jar파일을 만들었다. 그런데 logging/Test 패키지에 있는 모든 항목은 LogServer 클래스가 있는 jar파일 내부에 같이 두려고 한다.
이렇게 하는 이유는 여러 jar파일들 중에서 logging/Test에서 사용할 LogTest클래스가 이전 클래스 패스 상에서 jar파일에 있다면, 해당 LogTest클래스를 실행한다. 즉, 내가 원하는 클래스가 동작하지 않는다. 앞의 설명이 정확한지는 확인해보지 않았지만, 대충 이런 느낌일 것이다.
이는 ibm.com에 내용에 따르면,
"패키지 작성자는 패키지된 클래스들의 버전 영속성을 강화할 수 있다. 봉합은 보안 조치도 제공하여 코드 탬퍼링을 탐지한다."
좀 어려운 단어를 써서 쉽게 감이 잡히지 않는다. ㅡ.ㅡ;
이것 역시 manifest.mf에 설정되며 그 형식은 아래와 같다.
Name: 패키지경로/패키지명/
Sealed: true
Sealed항목이 true로 되어 있으면 된다. 그러면 해당 "패키지명"는 현재 jar파일 안에 포함되어 있어야 한다. 그렇다고 해서 jar가 자동으로 패키지를 포함해주지는 않은다. 사용자가 직접 입력해줘야한다. 입력된 내용은 아래 처럼 볼 수 있다.
실행은 살펴보지 않겠다. 필요하신 분은 직접 시도해보시길 바란다. ^^;
기타
다른 것으로는 패키지 정보를 추가하는 부분이다. 반드시 필요한 항목은 아니며 패키지 관리에 도움이 되는 부분이라 볼 수 있다. manifest.mf에 추가되며 그 형식은 아래와 같다.
Name: 패키지경로/패키지명/
Specification-Title: 패키지명
Specification-Version: 패키지 버전
Specification-Vendor: 패키지 제조 회사
Implementation-Title: 구현 패키지명
Implementation-Version: 구현버전(보통 빌드번호)
Implementation-Vendor: 구현 제조 회사
아래는 log4j의 manifest.mf파일 내용이므로 참고하길 바란다.
그다지 어려운 내용이 없기 때문에 그냥 넘어가겠다. 단지 스팩 만드는 부분과 실제 구현하는 부분이 분리되어서 추가되었다.
Expert
이번에는 좀더 어려운 부분으로 가보자. 이번에는 보안과 관련된 항목이다.
어떻게 보면 아주 중요한 항목이라 볼 수 있다. 보안은 실제 눈으로 보이는 효과는 없지만, 안보이는 곳에서의 전쟁을 승리로 이끄는 중요한 열쇠이다.
본인도 jar에 대한 보안을 이글을 작성하면서 바로 이해한 부분이라 정확하리라는 보장은 없어, 틀린 부분이 있다면 겸손하게(^^;) 지적해주길 바란다.
소개
jar에서 보안은 보통 "전자서명"을 하는 것이 보면 된다. 이렇게 하는 이유는 단순하다. 해당 파일이 내가 확인한 파일임을 명시하는 것이다. 즉 해당 서명이 틀리면 내가 확인한 파일이 아니므로 문제가 있다라고 말할 수 있다. 그렇기에 "전자서명"후에 해당 파일에 대한 확인 작업이 추가로 필요하게 된다.
사실 "전자서명"만으로는 부족하다. 다른 사람이 "전자서명"을 해두면 이 것이 정당한지 확인할 길이 없다. 그래서 필요한 것이 "인증센터"이다. 그래서 "전자서명"시 이를 인증할 "인증센터"를 명시한다. 이 센터는 아무나 만들수 없는 공인센터이야함은 당연하다.
예를 들어 MS-Sql의 jdbc를 살펴보자. "META-INF"디렉토리에 들어 있는 파일은 다음과 같다.
아래는 그 중에서 manifest.mf 파일 내용중 일부이다.
Manifest-Version: 1.0
Created-By: Signtool (signtool 1.3)
Comments: PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT.
Name: com/microsoft/sqlserver/jdbc/AppDTVImpl$SetValueOp.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: KrvkkdiHwdXHNmPcE9RQBQ==
SHA1-Digest: 6opr4+DKaQZj0Fr3FVaZUEm7jo8=
Name: com/microsoft/sqlserver/jdbc/AppDTVImpl.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: nDtY9xaWCEF+HnuZII0/mQ==
SHA1-Digest: DKIMpF6OfIdlemGRNzve/44H5Bc=
(생략)
위의 내용을 보면 인증 방식이 MD5 혹은 SHA1으로 되어 있음을 알 수 있다. 각 다이제스트는 jar에 있는 모든 파일에 대해 일일이 지정되어 있다. 클래스 파일 외에 이미지나 문서파일도 포함된다. 그렇다고 이 내용을 사용자가 전부 입력하는 건 아니다. 자동으로 생성해서 입력해준다. 이부분이 "전자서명"이라 보면 된다.
각각 파일에 있는 "전자서명"을 근거로 각 파일들의 다이제스트를 재계산해서 비교한다. 마지막으로 자신 manifest.mf 파일도 다이제스트를 재계산해서 서명파일에 있는 다이제스트와 비교한다. 서명파일이 확장자 "sf"가진 파일이다. 앞의 sqljdbc인 경우 zigbert.sf라는 이름으로 아래와 같은 내요이 들어 있다. 처음 항목이 manifest.mf에 대한 다이제스트라고 보면 된다.
Signature-Version: 1.0
Created-By: Signtool (signtool 1.3)
Comments: PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT.
Digest-Algorithms: MD5 SHA1
MD5-Digest: f2XP9lsoHe04PBOvuFXL5g==
SHA1-Digest: 8RHZ0uUrxScBNJtHgI/t7stA9yA=
Name: com/microsoft/sqlserver/jdbc/AppDTVImpl$SetValueOp.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: VLh2abTPHFQ9lSYvwTmrTA==
SHA1-Digest: UZ48R4Mgy46R1/HfJaTJ+Ygk1bU=
(생략)
그리고 이외에 사람이 읽을 수 없는 파일 하나가 있다. signature block 파일이다. 앞의 sqljdbc에서는 zigbert.rsa파일이다. 여기에는,
- 서명자의 개인키로 생성된 jar파일에 대한 전자서명
- 서명자의 공개키로 포함되어 저자서명에 대한 확인 작업시 사용
확장자 명이 rsa로 되어 있지만 dsa를 가질 수 있다. dsa는 DSA(Digital Signature Algorithm)을 사용한 방식이라는 말이다. 물론 확장자는 서명하는 종류에 따라서 달라질 것이다.
서명하기
jar파일에 서명하기 위해서는 공개키/개인키를 생성해야한다. 자신만의 서명이 있어야된다. 이때 사용하는 명령이 keytool이다. 아래 처럼 간단히 키를 생성하면 된다.
옵션 "genkey"는 "genkeypair"라는 옵션이다. 이는 키를 생성하겠다는 의미이다.
생성된 키는 파일로 저장되지 않은다. keystore이라는 데이터 자장공간에 넣어둔다. 이 안에 있는 키 목록을 보려면 아래와 같이 "list"옵션을 사용한 된다.
이제 jar파일에 서명을 해보자. 이때 사용하는 명령은 jarsigner이다. 이는 아직(ver 1.6.0) 한글화가 안되있다. ^^:
아래와 같이 사용법은 간단하다.
jarsigner jar파일명 별칭
jar파일명은 앞에서 생성한 jar파일을 넣어주면 된다. 별칭은 keytool에 의해 생성된 것으로서 앞 결과를 보면 mykey이다. 아래는 실행 결과이다.
경로로 해당 서명이 6개월 후면 보증기간이 만료가 된다고 하네요. ㅡ.ㅡ;
그냥 넘어가죠. (쿨럭 ㅡㅡ;)
이제 서명된 jar파일을 얻었습니다. 제대로 되었는지 jar파일 안을 살펴보겠습니다.
제대로 파일은 보이네요. 실제 파일 내용도 제대로 들어있는지 살펴볼까요.
잘 추가가 되었네요. 자 이정도로만 하죠. 더하면 머리아프고 관심있으신 분은 아래 영문자료를 참고해주길 바랍니다. (패스~ ㅡ.ㅡ;)
서명확인
서명을 했으니 서명을 확인해봐야죠. 이것 역시 jarsigner 명령을 이용하며 이때 사용하는 옵션이 "verify"입니다. 사용법은 단순히 아래와 같이 하면 되죠.
jarsigner -verify jar파일명
그럼 실제 결과를 보죠.
"verified"라고 "확인되었다"고 하네요.
만약 서명이 되었거나, 잘못되었다면 아래와 같이 나타날 것입니다.
기타
다른 내용으로 프로그래밍시 jar파일을 액세스할 수 있는 API를 제공해준다. 관련 패키지가 다음과 같다.
- java.util.jar
- java.net.JarURLConnection
- java.net.URLClassLoader
JarURLConnection클래스를 이용해서 원격에 있는 jar파일을 불러올 수 있으며, JarClassLoader클래스를 이용해서 jar파일을 실행할 수도 있다.
만약 jar파일을 원격에서 수신받는다면, 원하는 클래스가 맨 나중에 있다면 모든 jar파일이 수신되어야할 것이다. 이때 인덱스를 만듬으로서 원하는 곳을 먼저 받아서 실행할 수 있다.
생성법은 아래와 같다.
jar -i jar파일명
그러면 jar파일 안에 META-INF 디렉토리 안에 INDEX.LIST가 생성된다. 그러면 내부에 있는 모든 패키지, 클래스가 나열이 된다. 궁금하시면 직접 만들어서 확인해보시라.
지가나는 길
앞에서 서명된 파일에 인덱스를 만들면 INDEX.LIST가 추가된다. 그러나 이 파일은 서명이 안되었기에 서명확인 작업을 하면 이상은 없지만 전체파일에 대해 서명이 안되었다고 경고 메시지가 보일 것이다. 필요하다면 다시 서명하면된다. ^^
후기
처음에는 간단함 참고용 jar파일을 만들려고 했는데 우연하게 아래에 좋은 자료들이 보이면서 본의 아니게 내용이 늘어났다. 그리고 시간도 많이 걸렸다. 7월 21일에 작성한 건데 지금 23일이다. 짬짬이 시간내어서 작성하느라 시간도 걸리고, 처음 보는 내용도 있어서 바로 이해하면서 작성하느라 시행착도 걸렸다.
다른 분들도 이글을 읽으면서 여러분과 우리나라 소프트웨어 개발의 발전에 조금이나마 도움이 되었으면하는 바램이다. -ospace 080723
참고자료
http://java.sun.com/docs/books/tutorial/deployment/jar/manifestindex.html
http://www.ibm.com/developerworks/kr/library/j-jar/
'3.구현 > Java or Kotlin' 카테고리의 다른 글
Java 쓰레드 간단한 코드 예제 (0) | 2008.07.24 |
---|---|
Java 쓰레드 상태 (0) | 2008.07.24 |
Reactor 패턴의 예제 코드 (0) | 2008.07.17 |
SLF4J simple tutorial (0) | 2007.12.20 |
Java thread에서 IllegalMonitorStateException 예외 발생문제 (0) | 2007.11.28 |