{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "fatjar_cmd.ipynb", "provenance": [], "collapsed_sections": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "2PdthG0V6HA-" }, "source": [ "**출처**\n", "\n", "[https://dzone.com/articles/java-8-how-to-create-executable-fatjar-without-ide](https://dzone.com/articles/java-8-how-to-create-executable-fatjar-without-ide)" ] }, { "cell_type": "markdown", "metadata": { "id": "kXSxziiD6Htu" }, "source": [ "# 명령줄로 실행 가능한 Fat JAR 생성하기\n", "\n", "명령줄 만으로 실행 가능한 fat JAR 파일을 만들고 실행하고 싶으십니까? 필요한 기초 작업과 수행 방법을 확인하십시오.\n", "\n", "이 글은 추가 플러그인, IDE 또는 다른 도구를 사용하지 않고 순수한 명령 줄과 Java에서 Fat JAR (Java 아카이브 파일)을 만들 수 있는 가능성을 검토하는 내 블로그 게시물을 통합한 내용입니다.\n", "\n", "빌드 도구 ([Ant](http://ant.apache.org/), [Maven](https://maven.apache.org/) 또는 [Gradle](https://gradle.org/))의 세계에서는 명령줄에 대해 생각하는 것이 유용하지 않을 수도 있습니다. 가장 유명한 IDE (IntelliJ, Eclipse 또는 NetBeans)는 빌드 도구와 구현을 즉시 제공합니다. 그러나 명령줄 만 있고 인터넷에 액세스 할 수 없다고 가정해 보겠습니다.\n", "\n", "그러면 어떻게 하시겠습니까?" ] }, { "cell_type": "markdown", "metadata": { "id": "chTVxMib6Kbm" }, "source": [ "## 파트 1: ExecutableOne.jar 컴파일 ([GitHub](https://github.com/mirage22/executable-one))\n", "\n", "이 첫 번째 부분의 목표는 실행 가능한 JAR 파일을 만드는 것입니다. ExecutableOne.jar 이라고 부르겠습니다. 명령줄을 열어서 간단한 프로젝트 폴더를 생성해 보겠습니다. 예제 프로젝트 구조는 [Maven 표준 디렉토리 레이아웃](http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html) 구조를 따릅니다.\n", "\n", "```\n", "./libs\n", "./out\n", "./README.md\n", "./src\n", "./src/main\n", "./src/main/java\n", "./src/main/java/com\n", "./src/main/java/com/exec\n", "./src/main/java/com/exec/one\n", "./src/main/resources\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "cdLh0qs36Gbh", "outputId": "a135425d-e60c-490c-e78a-1cdf7452e2dc" }, "source": [ "!git clone https://github.com/mirage22/executable-one.git" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Cloning into 'executable-one'...\n", "remote: Enumerating objects: 46, done.\u001b[K\n", "remote: Counting objects: 100% (46/46), done.\u001b[K\n", "remote: Compressing objects: 100% (29/29), done.\u001b[K\n", "remote: Total 46 (delta 2), reused 46 (delta 2), pack-reused 0\u001b[K\n", "Unpacking objects: 100% (46/46), done.\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "aH__mLFm7Q3S" }, "source": [ "우리의 의도는 실행 가능한 JAR 파일을 만드는 것이므로 기본 클래스를 만들어야 합니다. com.exec.one 패키지에서 해봅시다. 패키지는 샘플 프로젝트 구조의 _SRC/MAIN/JAVA_ 폴더에서 찾을 수 있습니다.\n", "\n", "```java\n", "package com.exec.one;\n", "\n", "public class Main {\n", " public static void main(String[] args){ \n", " System.out.println(\"Main Class Start\"); \n", " } \n", "}\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "TXzyfxB96pqj", "outputId": "f210379b-01e2-47e7-c800-5635a76ac7e4" }, "source": [ "%%writefile executable-one/src/main/java/com/exec/one/Main.java\n", "package com.exec.one;\n", "\n", "public class Main {\n", " public static void main(String[] args){ \n", " System.out.println(\"Main Class Start\"); \n", " } \n", "}" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Overwriting executable-one/src/main/java/com/exec/one/Main.java\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "hTw9JLqy7VEN" }, "source": [ "_SRC/MAIN/RESOURCES_ 폴더 안에 META-INF 폴더를 만든 다음 그 안에 MANIFEST.MF 파일을 배치합니다. 새로 생성된 MANIFEST.MF 파일을 열고 기본 설명을 입력해 보겠습니다.\n", "\n", "```\n", "Manifest-Version: 1.0 \n", "Class-Path: . \n", "Main-Class: com.exec.one.Main\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "UDG_D8sR6tss", "outputId": "d75f07a9-3b76-44d4-9283-c3ffdf2aed9e" }, "source": [ "!cat executable-one/src/main/resources/META-INF/MANIFEST.MF" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Manifest-Version: 1.0\n", "Created-By: miragemiko (command line)\n", "Class-Path: .\n", "Main-Class: com.exec.one.Main\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "zbTDx8y27tDO" }, "source": [ "**참고** : JAR 파일 당 하나의 MANIFEST.MF 파일만 있습니다.\n", "\n", "MANIFEST.MF 파일에는 JAR 파일이 사용되는 방법에 대한 세부 사항이 포함되어 있습니다. 자세한 내용은 다루지 않습니다. 정의한 옵션에 집중하겠습니다.\n", "\n", "1. Manifest-Version : manifest 파일 버전입니다.\n", " \n", "2. 클래스 경로 : 애플리케이션 또는 확장 클래스 로더는 이 속성 값을 사용하여 내부 검색 경로를 구성합니다. 원래 클래스 로더는 검색 경로에서 각 요소를 다운로드하고 엽니다. 이러한 목적을 위해 간단한 선형 검색 알고리즘이 사용되었습니다.\n", " \n", "3. Main-Class : 시작시에 런처가 로드할 클래스의 이름이 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "zT-bIHpd7x1H" }, "source": [ "이제 \\*.jar 라이브러리없이 JAR 파일을 생성합니다. 프로젝트 구조의 _LIBS_ 폴더는 여전히 비어 있습니다. 이렇게 하려면 먼저 javac를 사용하여 프로젝트를 컴파일해야합니다. 한편 출력은 _OUT_ 폴더에 저장합니다. 명령 줄로 돌아가서 프로젝트 루트 안에 다음을 입력해 보겠습니다.\n", "\n", "```shell\n", "$javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java -d ./out/\n", "```" ] }, { "cell_type": "code", "metadata": { "id": "b8wnLZIS7dTV" }, "source": [ "!cd executable-one && javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java -d ./out/" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "LrEmXD8m8CTj" }, "source": [ "프로젝트가 _OUT_ 디렉터리로 컴파일되었습니다. ls 명령을 사용하여 확인할 수 있습니다.\n", "\n", "두 번째 단계는 _OUT_ 디렉터리에 있는 리소스에서 실행 가능한 JAR 파일을 만드는 것입니다. 명령줄로 돌아가서 다음 명령을 실행합니다.\n", "\n", "```shell\n", "$jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ .\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ziNcQ5b177Ye", "outputId": "3afa9093-72bd-420b-9364-8f8adea94088" }, "source": [ "!cd executable-one && jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ ." ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "added manifest\n", "adding: com/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/Main.class(in = 431) (out= 298)(deflated 30%)\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "UNVgheWW8MmQ" }, "source": [ "우리가 사용한 JAR 도구 옵션을 간단히 검토 및 설명해 보겠습니다.\n", "\n", "- c : 새 JAR 파일을 만들려고 함을 나타냅니다.\n", "- v : 표준 출력에 대한 자세한 출력을 생성합니다.\n", "- f : 생성 할 jar 파일을 지정합니다.\n", "- m : 우리가 사용하는 매니페스트 파일을 나타냅니다. 매니페스트 파일에는 이름-값 쌍이 포함됩니다.\n", "- \\-C : 디렉토리에 대한 임시 변경을 나타냅니다. 이 디렉토리에서 JAR 파일로 클래스가 추가됩니다. 점은 모든 클래스 (파일)를 나타냅니다.\n", "\n", "최종 출력을 위해 명령줄을 열고 다음을 입력합니다.\n", "\n", "```shell\n", "$java -jar ./ExecutableOne.jar\n", "\n", "standard output: \n", "Main Class Start\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "LBgTT_hO8JQ2", "outputId": "86e79556-766d-420f-9056-38548ef68c5c" }, "source": [ "!cd executable-one && java -jar ./ExecutableOne.jar" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Main Class Start\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "zFbXFW859c7d" }, "source": [ "잘 했습니다! 파트 2로 이동하겠습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "68Shrhno9dXF" }, "source": [ "## 파트 2: 추가적인 패키지와 함께 ExecutableOne.jar 컴파일\n", "\n", "이 섹션의 주요 목표는 추가적인 패키지를 포함하여 실행 가능한 JAR 파일을 컴파일 아는 방법을 보여드리겠습니다. 이러한 목적을 위해, 우리는 MagicService를 만들 것입니다. 이 서비스는 우리에게 getMessage() 메소드를 우리에게 제공하며 표준 출력으로 메세지를 출력합니다.\n", "\n", "명령줄을 열어 새로운 폴더 *SERVICE*와 파일 MagicService.java를 만듭니다.\n", "\n", "```shell\n", "$mkdir src/main/java/com/exec/one/service\n", "$vi src/main/java/com/exec/one/service/MagicService.java\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "gpR9zsrV94jr" }, "source": [ "새롭게 만들어진 MagicService는 다음 예제에서 사용될 수 있습니다.\n", "\n", "```java\n", "package com.exec.one.service; \n", "public class MagicService { \n", " private final String message; \n", " public MagicService(){ \n", " this.message = \"Magic Message\";\n", " } \n", "\n", " public String getMessage(){ \n", " return message; \n", " }\n", "}\n", "```\n" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "15WNp0Pl8sBG", "outputId": "0da49379-df9a-4683-a87e-3d625a265759" }, "source": [ "!mkdir -p executable-one/src/main/java/com/exec/one/service\n", "!cat executable-one/src/main/java/com/exec/one/service/MagicService.java" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "package com.exec.one.service;\n", "\n", "public class MagicService {\n", "\n", " private final String message;\n", " public MagicService(){\n", " this.message = \"Magic Message\";\n", " }\n", "\n", " public String getMessage(){\n", " return message;\n", " }\n", "\n", "}\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "EZLobrXr9-QY" }, "source": [ "MagicService는 Main 클래스보다 패키싲 구조에서 다른 위치에 있습니다. 이제 우리는 Main 클래스로 돌아가서 새롭게 만든 MagicService를 import 합니다. import하고 서비스 인스턴스를 만든 뒤에 Main 클래스는 getMessage() 메소드로 접근을 할 것입니다. Main 클래스는 다음 방법으로 변경될 것입니다.\n", "\n", "```java\n", "package com.exec.one; \n", "import com.exec.one.service.MagicService; \n", "public class Main { \n", " public static void main(String[] args){\n", " System.out.println(\"Main Class Start\"); \n", " MagicService service = new MagicService(); \n", " System.out.println(\"MESSAGE : \" + service.getMessage());\n", " }\n", "} \n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "MnezQFz690Xj", "outputId": "5b679136-af6e-45f0-d2c6-1d5e02b367ae" }, "source": [ "%%writefile executable-one/src/main/java/com/exec/one/Main.java\n", "package com.exec.one;\n", "import com.exec.one.service.MagicService; \n", "public class Main {\n", " public static void main(String[] args){ \n", " System.out.println(\"Main Class Start\");\n", " MagicService service = new MagicService(); \n", " System.out.println(\"MESSAGE : \" + service.getMessage()); \n", " } \n", "}" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Overwriting executable-one/src/main/java/com/exec/one/Main.java\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "lxpF3YDr-L-r" }, "source": [ "이제 코드를 컴파일 할 준비가 된 지점에 도달했습니다. 명령줄로 돌아가 Executable-One 프로젝트의 루트 폴더로 이동하겠습니다. 첫 번째 단계는 Executable-One 프로젝트를 OUT 폴더로 컴파일 / 재컴파일하는 것입니다. 이를 위해 새로 생성된 MagicService.java 클래스의 위치를 추가해야 합니다.\n", "\n", "```shell\n", "javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java -d ./out/\n", "```" ] }, { "cell_type": "code", "metadata": { "id": "vVvpHElH-LJR" }, "source": [ "!cd executable-one && javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java -d ./out/" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "NtnGr7RG-kVf" }, "source": [ "두 번째 단계는 컴파일된 클래스에서 실행 가능한 JAR 파일을 만드는 것입니다. JAR 파일 논리를 변경하지 않았으므로 명령을 변경할 필요가 없습니다. 이는 MANIFEST.MF 파일이 변경없이 그대로 유지됨을 의미합니다.\n", "\n", "```\n", "Manifest-Version: 1.0\n", "Class-Path: . \n", "Main-Class: com.exec.one.Main\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "j9_A39RB-lIb" }, "source": [ "이제 샘플 프로젝트의 루트 디렉토리에서 Part 1과 유사한 명령을 다시 실행할 수 있습니다.\n", "\n", "```shell\n", "jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ .\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ZIc7cqoa-fr0", "outputId": "e99ba1c6-c053-4472-ffa1-c0551b8227df" }, "source": [ "!cd executable-one && jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ ." ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "added manifest\n", "adding: com/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/service/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/service/MagicService.class(in = 380) (out= 265)(deflated 30%)\n", "adding: com/exec/one/Main.class(in = 1009) (out= 560)(deflated 44%)\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "2mYeyHzA-rjq" }, "source": [ "생성된 JAR 파일을 실행하여 표준 출력에 인쇄된 메시지를 얻습니다.\n", "\n", "```shell\n", "$java -jar ExecutableOne.jar \n", "output: \n", "Main Class Start\n", "MESSAGE : Magic Message\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Pa6y31zx-pZG", "outputId": "7ac91981-b8f2-491e-980d-51adc33d27b5" }, "source": [ "!cd executable-one && java -jar ExecutableOne.jar " ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Main Class Start\n", "MESSAGE : Magic Message\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "qAzKbQw8-znv" }, "source": [ "축하합니다. 다시 잘 하셨습니다!" ] }, { "cell_type": "markdown", "metadata": { "id": "bU3R9A1h_fF_" }, "source": [ "## 파트 3: 실행 가능한 Fat JAR 생성하기 ([GitHub](https://github.com/mirage22/executable-two))\n", "\n", "이 파트의 목표는 개발된 프로그램에서 모든 필요한 의존성을 포함하는 fat JAR(Java Archive)를 생성하는 것입니다. 외부 라이브러리로 우리는 파트 2에서 생성된 JAR 파일을 사용할 필요가 있습니다. 파트 3에서 우리는 (위의 링크에서 다운로드 받을 수 있는) \"Executable-Two\"라고 불리는 샘플 프로젝트를 생성합니다.\n", "\n", "executable-two 프로젝트는 다음과 같은 폴더 구조를 가집니다.\n", "\n", "```\n", "./libs\n", "./libs/ExecutableOne.jar\n", "./out\n", "./README.md\n", "./src\n", "./src/main\n", "./src/main/java\n", "./src/main/java/com\n", "./src/main/java/com/exec\n", "./src/main/java/com/exec/two\n", "./src/main/java/com/exec/two/Main.java\n", "./src/main/resources\n", "./src/main/resources/META-INF\n", "./src/main/resources/META-INF/MANIFEST.MF\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "RwIYC9Yo-vWU", "outputId": "9ad4ff5a-acfa-4182-e757-2f3fa00aa134" }, "source": [ "!git clone https://github.com/mirage22/executable-two.git" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Cloning into 'executable-two'...\n", "remote: Enumerating objects: 16, done.\u001b[K\n", "remote: Counting objects: 6% (1/16)\u001b[K\rremote: Counting objects: 12% (2/16)\u001b[K\rremote: Counting objects: 18% (3/16)\u001b[K\rremote: Counting objects: 25% (4/16)\u001b[K\rremote: Counting objects: 31% (5/16)\u001b[K\rremote: Counting objects: 37% (6/16)\u001b[K\rremote: Counting objects: 43% (7/16)\u001b[K\rremote: Counting objects: 50% (8/16)\u001b[K\rremote: Counting objects: 56% (9/16)\u001b[K\rremote: Counting objects: 62% (10/16)\u001b[K\rremote: Counting objects: 68% (11/16)\u001b[K\rremote: Counting objects: 75% (12/16)\u001b[K\rremote: Counting objects: 81% (13/16)\u001b[K\rremote: Counting objects: 87% (14/16)\u001b[K\rremote: Counting objects: 93% (15/16)\u001b[K\rremote: Counting objects: 100% (16/16)\u001b[K\rremote: Counting objects: 100% (16/16), done.\u001b[K\n", "remote: Compressing objects: 14% (1/7)\u001b[K\rremote: Compressing objects: 28% (2/7)\u001b[K\rremote: Compressing objects: 42% (3/7)\u001b[K\rremote: Compressing objects: 57% (4/7)\u001b[K\rremote: Compressing objects: 71% (5/7)\u001b[K\rremote: Compressing objects: 85% (6/7)\u001b[K\rremote: Compressing objects: 100% (7/7)\u001b[K\rremote: Compressing objects: 100% (7/7), done.\u001b[K\n", "remote: Total 16 (delta 1), reused 16 (delta 1), pack-reused 0\u001b[K\n", "Unpacking objects: 6% (1/16) \rUnpacking objects: 12% (2/16) \rUnpacking objects: 18% (3/16) \rUnpacking objects: 25% (4/16) \rUnpacking objects: 31% (5/16) \rUnpacking objects: 37% (6/16) \rUnpacking objects: 43% (7/16) \rUnpacking objects: 50% (8/16) \rUnpacking objects: 56% (9/16) \rUnpacking objects: 62% (10/16) \rUnpacking objects: 68% (11/16) \rUnpacking objects: 75% (12/16) \rUnpacking objects: 81% (13/16) \rUnpacking objects: 87% (14/16) \rUnpacking objects: 93% (15/16) \rUnpacking objects: 100% (16/16) \rUnpacking objects: 100% (16/16), done.\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "9QJ1KsWY_oRZ" }, "source": [ "LIBS 폴더는 전에 생성된 \"ExecutableOne.jar\"를 포함합니다. \"ExecutableOne.jar\"는 우리가 ExecutableTwo 안에서 사용될 MagicService 클래스를 포함합니다. 우리는 클래스 MagicService를 인스턴스화 하여 public 메소드 getMessage()를 실행할 것입니다. 프로젝트 \"ExecutableTwo\"의 메인 클래스 안에서 이 모든 것이 발생할 것입니다.\n", "\n", "프로젝트 패키지 com.exec.two 에서 다음 메인 클래스를 생성합시다.\n", "\n", "```java\n", "package com.exec.two; \n", "import com.exec.one.service.MagicService; \n", "public class Main {\n", " public static void main(String[] args){\n", " System.out.println(\"Executable-Two Main\");\n", " MagicService service = new MagicService();\n", " System.out.println(\"MagicService from Executable-ONE\");\n", " System.out.println(\"MESSAGE: \" + service.getMessage());\n", " }\n", "}\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "tNed8AdH_jiN", "outputId": "5cb8abb7-789c-4e24-9576-c5897ba5c775" }, "source": [ "!cat executable-two/src/main/java/com/exec/two/Main.java" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "package com.exec.two;\n", "\n", "import com.exec.one.service.MagicService;\n", "\n", "public class Main {\n", "\n", " public static void main(String[] args){\n", "\n", " System.out.println(\"Executable-Two Main\");\n", " MagicService service = new MagicService();\n", " System.out.println(\"MagicService from Executable-ONE\");\n", " System.out.println(\"MESSAGE: \" + service.getMessage());\n", "\n", " }\n", "\n", "}\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "jd0XdXFhAU36" }, "source": [ "이제 우리는 JAR 파일 생성의 모든 것이 준비 되었습니다. 이전에 생성한 getMessage() 메소드를 실행한 JAR 라이브러리에서 MagicService를 가져 왔습니다. 다음 몇 단계에서는 Java JDK에서 제공하는 javac 및 JAR 도구를 사용합니다. 명령 줄로 돌아가서 프로젝트를 컴파일해 보겠습니다. 명령에서 클래스 경로를 사용된 라이브러리로 확장해야 함을 컴파일러에 알려야합니다.\n", "\n", "```shell\n", "$javac -cp ./src/main/java \n", "./src/main/java/com/exec/two/*.java -d ./out/ \n", "-classpath ./libs/ExecutableOne.jar\n", "```" ] }, { "cell_type": "code", "metadata": { "id": "P97RkfJE_ydY" }, "source": [ "!cd executable-two && javac -cp ./src/main/java ./src/main/java/com/exec/two/*.java -d ./out/ -classpath ./libs/ExecutableOne.jar" ], "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "9oJeQcazCEW-", "outputId": "b9e65fdb-19c5-499b-8549-e7467f5adf7f" }, "source": [ "!cd executable-two/out && find . -name *.class" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "./com/exec/two/Main.class\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "0AbVzKNWAlnK" }, "source": [ "\"Executable-Two\" 프로젝트가 OUT 디렉토리로 성공적으로 컴파일 되었습니다.\n", "\n", "이제 fat JAR 생성을 위해 OUT 디렉토리를 적절하게 준비할 때입니다. OUT 디렉토리 안에는 \"Executable-Two\"를 위해 만든 컴파일 된 클래스가 있습니다. 한편, JAR 도구는 파일 시스템에 물리적으로 위치한 파일만 읽습니다. 압축된 JAR 파일은 읽지 않습니다. 물론 이는 JAR 도구가 OUT 디렉토리에있는 \\* .jar 파일의 압축을 풀거나 읽지 않음을 의미합니다.\n", "\n", "그 결과 ExecutableOne.jar을 OUT 디렉토리에 복사하더라도 JAR 도구는 ExecutableOne.jar 파일의 압축을 풀지 않고 라이브러리가 결과에 추가됩니다 (압축된 형태로). 물론 압축 되었기 때문에 무시됩니다.\n", "\n", "문제는 $java -jar 도구가 내부 패키지 \\*.jar 아카이브 파일을 읽지 않는다는 것입니다!\n", "\n", "이는 이전에 생성된 Java 아카이브 (JAR) \"_Executable-One.jar_\"을 \"Executable-Two\" 프로젝트의 OUT 디렉토리에 압축 해제해야 함을 의미합니다. 명령 줄을 열고 다음을 입력합니다.\n", "\n", "```shell\n", "$cp libs/ExecutableOne.jar ./out/\n", "$cd ./out\n", "$tar xf ExecutableOne.jar\n", "$rm ExecutableOne.jar\n", "```" ] }, { "cell_type": "code", "metadata": { "id": "iQwjprzcAcKb" }, "source": [ "!cd executable-two && cp libs/ExecutableOne.jar ./out/\n", "!cd executable-two/out && jar xf ExecutableOne.jar\n", "!cd executable-two/out && rm ExecutableOne.jar" ], "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "VOwHME4gA6up", "outputId": "46a1091c-75ed-4f3a-b05f-b59c2a3cb079" }, "source": [ "!cd executable-two/out && find . -name *.class" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "./com/exec/one/service/MagicService.class\n", "./com/exec/one/Main.class\n", "./com/exec/two/Main.class\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "oGv_M416CWKL" }, "source": [ "이제 \"Executable-Two\" 프로젝트 출력 디렉토리를 새 JAR 파일의 소스 폴더로 사용할 준비가 되었습니다.\n", "\n", "**참고**: 모든 실행 가능한 JAR 파일에는 하나의 _MANIFEST.MF_ 파일만 사용할 수 있습니다.\n", "\n", "\"Executable-Two\"프로젝트를 JAR 아카이브 파일에 묶기 위해 ./src/main/resources/META-INF/ 폴더에 새로 생성된 매니페스트 파일을 사용합니다.\n", "\n", "```\n", "Manifest-Version: 1.0 \n", "Class-Path: . \n", "Main-Class: com.exec.two.Main\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "sVKEs4mRCeW5", "outputId": "08410c7d-3dfd-4970-b189-749c642d6bb8" }, "source": [ "!cat executable-two/src/main/resources/META-INF/MANIFEST.MF" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Manifest-Version: 1.0 Class-Path: . \n", "Main-Class: com.exec.two.Main \n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "QWLC7UxTCWmA" }, "source": [ "다음처럼 타이핑하여 이 모두를 묶을 수 있습니다..\n", "\n", "```shell\n", "$jar cvfm ExecutableTwo.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ .\n", "```\n" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "MU9lGwxBBw7m", "outputId": "44f6e873-43a6-4730-fbcd-55db17ae6893" }, "source": [ "!cd executable-two && jar cvfm ExecutableTwo.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ ." ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "added manifest\n", "adding: com/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/service/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/one/service/MagicService.class(in = 380) (out= 265)(deflated 30%)\n", "adding: com/exec/one/Main.class(in = 703) (out= 441)(deflated 37%)\n", "adding: com/exec/two/(in = 0) (out= 0)(stored 0%)\n", "adding: com/exec/two/Main.class(in = 1061) (out= 586)(deflated 44%)\n", "ignoring entry META-INF/\n", "ignoring entry META-INF/MANIFEST.MF\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "wy_E0_kECv7q" }, "source": [ "새로 생성된 fat JAR 파일인 \"ExecutableTwo.jar\"을 실행하면 다음과 같은 출력이 나타납니다.\n", "\n", "```shell\n", "$java -jar ./ExecutableTwo.jar\n", "output:\n", "Executable-Two Main\n", "MagicService from Executable-ONE\n", "MESSAGE: Magic Message\n", "```" ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Mk1NBIBdCboN", "outputId": "ee3cebf3-06e8-4403-9a1b-9bb8f370814d" }, "source": [ "!cd executable-two && java -jar ./ExecutableTwo.jar" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Executable-Two Main\n", "MagicService from Executable-ONE\n", "MESSAGE: Magic Message\n" ], "name": "stdout" } ] } ] }