spring
jar 내부의 manifest 본문 읽어오기

jar 내부의 manifest 본문 읽어오기

java 언어로 프로젝트를 진행 중 manifest 내부에 정보를 저장하고 빌드 후 .jar 파일에서 정보를 읽어와야하는 경우가 발생하였습니다. 이에 대하여 해결한 방법을 공유하고자 글을 씁니다.

개발 환경

  • open jdk 11
  • vscode
  • spring boot
  • gradle

예시

예시로 gradle 환경에서 manifest에 어플리케이션 버전 정보를 저장하고 REST API와 커맨드 라인 명령어를 통하여 버전 정보를 읽어올 수 있는 코드를 작성해 보겠습니다.

현재 개발중인 또는 테스트를 위한 프로젝트가 생성되어있다고 가정하겠습니다.

1.gradle을 통한 manifest에 버전 정보 저장

build.gradle 을 IDE 또는 본인이 사용하시에 편리한 편집기로 열어서 아래와 같은 코드를 추가합니다.
저는 VSCODE를 이용하여 편집하였습니다.

bootJar {
    archiveBaseName = 'manifest_control_example'
    manifest {
        attributes(
            "Application-Version" : "1.0.0"
        )
    }
}

위 코드를 작성하면 gradle을 통한 빌드 시 manifest_control_example.jar 라는 이름의 .jar가 생성되고 .jar 내부의 META-INF/MANIFEST.MF 파일을 편집기로 열어보면 Application-Version: 1.0.0 이라는 텍스트가 입력된 것을 확인하실 수 있을 겁니다.
이와 같이 Application-Vesion 이외에 빌드 날짜, 개발 환경 정보, 숨겨진 연애편지(?)등 원하는 정보를 작성할 수 있습니다.

이제는 쓰는법을 알았으니 원래 목적인 읽는 법을 알아보겠습니다.

2. manifest 정보 읽어오기

프로젝트 내부에 속하는 클래스 중 어떠한 클래스에도 상관 없지만 저는 편의를 위하여
ManifestController.java 라는 이름의 클래스를 만들었고 해당 클래스 내부에서 manifest의 정보를 읽어오는 코드를 작성해보겠습니다.

    public class ManifestController {
 
        private Manifest manifest;
 
        public ManifestController() throws MalformedURLException, IOException {
            String className = ManifestController.class.getSimpleName() + ".class";
            String classPath = ManifestController.class.getResource(className).toString();
            if (!classPath.startsWith("jar:file:")) {
                return;
            }
            URL url = new URL(classPath);
            JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
            manifest = jarConnection.getManifest();
        }
 
        public String getApplicationVersion() {
            if (manifest == null) {
                return null;
            }
            return manifest.getMainAttributes().getValue("Application-Version");
        }
    }   

위와 같은 코드를 통해서 manifest에서 원하는 정보를 읽어올 수 있습니다.
먼저 .jar 내부에 포함될 아무 클래스나 상관없지만 너무 쌩뚱맞은 클래스를 이용하면 가독성이 떨어지니 현재 클래스인 ManifestController 클래스의 경로를 얻어옵니다.
이때 주의할 점은 빌드 후 .jar 에 담겨있을 클래스의 경로와 IDE 디버깅 상에서 class의 경로는 다르기 때문에 디버깅 상에서는 정상적인 .jar의 경로를 얻을 수 없으며 빌드 후에도 if문을 통하여 경로의 시작점이 jar인지 다시 한번 확인합니다.

정상적으로 경로가 얻어졌다면 if문을 통과할 것이고 얻어온 classPath 정보는 URL 클래스의 생성 인수로 사용되어 URL 클래스 객체를 생성하게 됩니다. 이 URL클래스 객체로 부터 openConnection()이라는 함수를 통하여 URLConnection 객체를 얻을 수 있게되는데 JarURLConnection 클래스는 URLConnection 클래스를 상속받은 하위 클래스이기에 형변환(이때의 형변환은 다운캐스팅)이 이루어질 수 있습니다.
이 JarURLConnection 객체는 getManifest() 라는 함수를 지원하며 해당 함수를 통하여.jar의 Manifest 객체를 얻을 수 있습니다.
이 Manifest 객체를 필드에 저장해놓으면
ManifestController의 생성자를 통하여 객체를 생성한 어딘가에서 getApplicationVersion() 함수를 이용하면 Application-Version 정보를 얻어올 수 있게 됩니다.

3. 커맨드 라인(Command Line)을 통하여 Manifest 정보 확인하기

이제 이 manifest 객체를 활용해볼 차례입니다.
먼저 커맨드 라인(Command Line)을 통하여 정보를 확인해보겠습니다.

    public class Application {
 
        public static void main(String[] args) throws IOException {
            if (args.length > 0) {
                for (String arg : args) {
                    if (arg.equalsIgnoreCase("-v") || arg.equalsIgnoreCase("-version")) {
                        ManifestController manifestController = new MainfestController();
                        System.out.println("Application-Version : " + manifestController.getApplicationVersion();
                        return;
                    }
                }
                return;
            }
            SpringApplication.run(Application.class, args);
        }
    }

위와 같이 main() 함수에 코드를 추가할 경우
터미널 창에서 ./java -jar manifest_control_example.jar -v 또는 ./java -jar manifest_control_example.jar -version 이라는 명령줄을 통하여 manifest로부터 Application-Version 정보를 가져올 수 있게 됩니다.

4. REST API 를 통하여 Manifest 정보 확인하기

졸음이 쏟아져 글이 점점 짧아짐을 느끼고 차후에 추가할 예정입니다..
한번이라도 REST API를 구현해보신 분이라면 어렵지 않게 구현하실 수 있으실거라 생각합니다..!