SW-PRODUCT/개발

Java로 Stand-Alone 프로그램 만들기

굴돌 2015. 1. 26. 16:48



최근 업무에서 동료가 Ruby로 스크립트 만들어서 간단히 db 조작하는 일을 처리한걸 봤다.

딱히 새로울 것은 없었지만,

개인적으로 오래 전에 script로 batch 작업들 만들어서 주기적으로 db 조작하는 방식을 선호하는 팀에서 일하면서

너무 장애가 일어나기 쉽고, 테스트 안되고, 코드관리 안되는걸 봐놔서

db업데이트를 스크립트로 안하는 경향이 강해지면서 반대로 script로 간단히 끝낼것도 안하게 됐다는 생각이 들었다.


그래서 script를 볼까 하다가... 요즘 세상이 좋아져서 java로도 간단히 돌릴 수 있겠다 싶어서 Java 기반으로 script 수준의 작업할 방법을 찾아봤다...

오래전에는 classpath랑 dependency 걸린 라이브러리 배포 문제로 골치 아팠었는데, 오래전에 maven 생기면서 해결됐으니...




1. maven exec:java

링크: http://www.vineetmanohar.com/2009/11/3-ways-to-run-java-main-from-maven/


mvn exec:java -Dexec.mainClass="package.MainClass" -Dexec.args="arg0 arg1 arg2"


maven에서 아래 플러그인 설정 하나만 해주면 알아서 dependency 관리하면서 실행할 수 있게 만들어준다.

    <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.3.2</version>
      </plugin>
    </plugins>
  </build>


장점은 설정이 간단하다.

단점은 실행환경에서도 maven 설치가 되어 있어야 하고, (아마도)소스가 통으로 복사되어있어야 한다.



maven: lib에 모아두고 manifest.mf 파일 잘 적어주기

maven-dependency-plugin과 maven-jar-plugin을 이용해서

특정 디렉토리(보통은 /lib)에 dependency 걸린 jar를 모두 넣어둔 후에

MEAT-INF/MENIFEST.MF 파일에다가 아래 두 항목을 적절히 기록해주는 방법이다.


실행할 jar 파일과 lib 디렉토리를 묶어서 배포를 하고,

실행할 곳에서 압축 풀어서 java -jar xxx.jar  명령어로 실행시킬 수 있다.


Main-Class: simplesa.Application
Class-Path: lib/commons-lang3-3.3.2.jar ...


아래 플러그인 설정만 해두면 아래 커맨드 만으로도 MENIFEST.MF 파일을 알아서 잘 만들어줘서 jar 명령어로 실행 가능해진다.


mvn clean package

cd target;tar cvf simplesa.tar simplesa-0.0.1-SNAPSHOT.jar lib

#ㄴ 이렇게 생성된 simplesa.tar 파일을 적정한곳에 복사

tar xvf simplesa.tar

java -jar simplesa-0.0.1-SNAPSHOT.jar


아래와 같이 maven 설정을 해줘야 하는데, MainClass 지정에 주의해야한다.

<build>
        <plugins>
                <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-dependency-plugin</artifactId>
                        <executions>
                                <execution>
                                        <id>copy-dependencies</id>
                                        <phase>prepare-package</phase>
                                        <goals>
                                                <goal>copy-dependencies</goal>
                                        </goals>
                                        <configuration>
                                                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                                                <overWriteReleases>false</overWriteReleases>
                                                <overWriteSnapshots>false</overWriteSnapshots>
                                                <overWriteIfNewer>true</overWriteIfNewer>
                                        </configuration>
                                </execution>
                        </executions>
                </plugin>
                <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <configuration>
                                <archive>
                                        <manifest>
                                                <addClasspath>true</addClasspath>
                                                <classpathPrefix>lib/</classpathPrefix>
                                                <mainClass>simplesa.Application</mainClass>
                                        </manifest>
                                </archive>
                        </configuration>
                </plugin>
        </plugins>
</build>


장점: 모든 절차가 직관적이다. => no Magic!

단점: tar로 묶어 옮기고 풀어주는 절차상의 번거로움. => No one-Stop


MANIFEST.MF 파일에 class-path 지정 기능과 Main-Class 지정 기능이 있는 점을 이용해, 최소한의 수고로 기본 spec을 활용해 배포 파일을 만들 수 있다.



Spring-boot를 이용해서 하나의 jar 파일로 묶기

위 방법은 다 좋은데...표준방법인 것도 좋은데... 어쨌든 tar를 직접 만들어줘야 한다.

maven 플러그인에서 tar(or zip)파일 만들어주는 것이 있긴 하지만... 그렇다 해도 실행할 곳에서 압축 풀어서 실행해야 한다....뭐 큰 문제는 아니겠지만서도..;;


Spring-boot에서는 이런 문제를 해결하기 위해

org.springframework.boot.loader.JarLauncher

라는 놈을 만들었다.

아래와 같이 설정하고 mvn package를 수행하면, JarLauncher를 이용하는 jar 파일로 재구성한다.

main-class도 알아서 찾아준다. 다만, main()이 여러개일 수도 있으므로 <Main-Class> 옵션으로 지정해서 선택 가능하다.


<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>1.2.1.RELEASE</version>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>   


아래와 같은 간단한 절차로 하나의 덩치 큰 jar 파일이 만들어진다.


mvn clean package

java -jar xxx.jar


동작원리는 간단하다.


일단 MANIFEST.MF 파일을 보면 아래와 같이 되어 있다.


Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: jpascript.Application


Main-class가 내가 만든 클래스가 아니고 JarLauncher가 된다.

만들어진 jar를 풀어보면 lib 디렉토리 밑에 dependency jar 파일들이 있을 뿐만 아니라

org/springframework/boot/loader/ 디렉토리 밑에 여러 파일들이 들어가 있다.

요게 284K 정도 된다.

용량은 별로 크진 않음.


java -jar xxx를 할 때 실제 실행은 JarLauncher가 실행되며, JarLauncher가 내가 만든 jpascript.Application을 실행시켜준다.


jar 파일 하나만 복사하면 배포 끝이고,

java -jar xxx.jar 형태로 실행하니 실행도 쉽다.




maven 플러그인 : 관련 jar 다 풀어서 하나의 jar로 다시 묶기

maven 플러그인 중에 모든 dependency jar들을 다 풀어서

하나의 jar 파일로 다시 묶어주는 플러그인이 있다.


딸랑 파일 하나라는 점은 위 방법들에서도 있지만,

META-INF 디렉토리를 공유한다던가, classpath root에 파일이 있는 경우 등등 다른 jar가 같은 경로에 파일을 보관할 때 파일들이 뒤섞이는 단점이 있다.


인터넷 뒤질때 종종 보였는데 지금 막상 찾으려니 안보여서 샘플은 패스....흠..아마도 요것인듯 http://stackoverflow.com/a/9415684


옜날글 찾았다 : http://blog.daum.net/rollin/8097007

ㄴ shade 플러그인으로 하나의 jar 파일 만드는 방법이네...






One-Jar 프로젝트

링크: http://one-jar.sourceforge.net/
maven jar 플러그인이나 spring-boot으로 잘 동작해서

자세히는 안봤다..;;



mvn exec:java -Dexec.mainClass="com.vineetmanohar.module.Main" -Dexec.args="arg0 arg1 arg2"   - See more at: http://www.vineetmanohar.com/2009/11/3-ways-to-run-java-main-from-maven/#sthash.CXkuNduc.dpuf