Tagged: jar
How to create an executable helloworld.jar
Let’s say you’re in the final stage for a job.
You’re instructed to create an executable jar file whose main method prints hello, world
following a line separator. And you have 600 seconds to do that.
If you think about ant
, maven
, or gradle
, you’re already late.
edit HelloWorld.java
$ vi HelloWorld.java $ cat HelloWorld.java public class HelloWorld { public static void main(final String... args) { System.out.printf("hello, world%n"); } } $ javac HelloWorld.java java HelloWorld hello, world
edit HelloWorld.mf
$ vi HelloWorld.mf $ cat HelloWorld.mf Main-Class: HelloWorld $
create the jar
$ jar cmf HelloWorld.mf helloworld.jar HelloWorld.class $ java -jar helloworld.jar hello, world $
How fast can you do that?
Executable Jar with Apache Maven
Apache Maven을 이용하여 실행 가능한 jar파일을 만들어 보자구!!!
References
GitHub
$ git clone git@github.com:jinahya/executable-jar-with-maven-example.git ... $ cd executable-jar-with-maven-example/ $ git checkout develop $ mvn clean package ... $ ls -l target/ ...
Exec Maven Plugin
FYI, Apache Maven을 이용하여 직접 main(String[])
메서드를 실행할 수 있다.
$ mvn -quiet exec:java -Dexec:mainClass=... ... $
Maven Jar Plugin
우선 Maven Dependency Plugin을 사용하여 실행에 필요한 dependency들을 target/${project.build.finalName}.lib/
에 복사한다.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/${project.build.finalName}.lib</outputDirectory> <includeScope>runtime</includeScope> </configuration> </execution> </executions> </plugin>
그리고 Maven Jar Plugin을 다음과 같이 설정하여 위에서 내려받은 라이브러리들의 위치를 META-INF/MANIFEST.MF
의 Class-Path
항목에 추가한다.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>${project.build.finalName}.lib/</classpathPrefix> <mainClass>${fully.qualified.main.class}</mainClass> </manifest> </archive> </configuration> </plugin>
$ ls -1F target/ ... executable-jar-with-maven-example-x.y.z.jar executable-jar-with-maven-example-x.y.z.lib/ ... $ unzip -p target/executable-jar-with-maven-example-x.y.z.jar META-INF/MANIFEST.MF ... Class-Path: executable-jar-with-maven-example-x.y.z.lib/guava-18.0.jar e xecutable-jar-with-maven-example-x.y.z.lib/guice-4.0-beta5.jar executab le-jar-with-maven-example-x.y.z.lib/javax.inject-1.jar executable-jar-w ith-maven-example-x.y.z.lib/aopalliance-1.0.jar ... Main-Class: ... $ ls -1F target/executable-jar-with-maven-example-x.y.z.lib/ ....jar ....jar ....jar ....jar $ java -jar target/executable-jar-with-maven-example-x.y.z.jar ... $
다음과 같이 배포 가능한 어카이브들을 생성한다.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <id>antrun-archive</id> <phase>package</phase> <goals> <goal>run</goal> </goals> <configuration> <target> <zip destfile="${project.build.directory}/${project.build.finalName}.zip"> <zipfileset dir="${project.build.directory}"> <include name="${project.build.finalName}.${project.packaging}" /> <include name="${project.build.finalName}.lib/*" /> </zipfileset> </zip> <tar destfile="${project.build.directory}/${project.build.finalName}.tar"> <tarfileset dir="${project.build.directory}"> <include name="${project.build.finalName}.${project.packaging}" /> <include name="${project.build.finalName}.lib/*" /> </tarfileset> </tar> <tar destfile="${project.build.directory}/${project.build.finalName}.tgz" compression="gzip"> <tarfileset dir="${project.build.directory}"> <include name="${project.build.finalName}.${project.packaging}" /> <include name="${project.build.finalName}.lib/*" /> </tarfileset> </tar> <tar destfile="${project.build.directory}/${project.build.finalName}.tbz2" compression="bzip2"> <tarfileset dir="${project.build.directory}"> <include name="${project.build.finalName}.${project.packaging}" /> <include name="${project.build.finalName}.lib/*" /> </tarfileset> </tar> </target> </configuration> </execution> </executions> </plugin>
Maven Assembly Plugin
Maven Assembly Plugin의 jar-with-dependencies descriptor를 사용하여 필요한 클래스들을 모두 포함하는 아카이브를 만들 수 있다.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.4</version> <configuration> <archive> <manifest> <mainClass>${fully.qualified.class.name}</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
$ ls -1F target/ ... executable-jar-with-maven-example-x.y.z-jar-with-dependencies.jar ... $ java -jar target/executable-jar-with-maven-example-x.y.z-jar-with-dependencies.jar ...
Maven Shade Plugin
다음은 Maven Shade Plugin을 이용하는 방법이다.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <configuration> <shadedArtifactAttached>true</shadedArtifactAttached> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>${fully.qualified.class.name}</mainClass> </transformer> </transformers> </configuration> <executions> <execution> <id>shade</id> <!--phase>package</phase--> <!-- default --> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin>
$ mvn clean package ... $ ls -1 target/ ... executable-jar-with-maven-example-x.y.z-shaded.jar ... $ java -jar target/executable-jar-with-maven-example-x.y.z-shaded.jar ... $
OneJar Maven Plugin
다음은 onejar-maven-plugin을 사용하는 방법이다.
<plugin> <!--groupId>org.dstovall</groupId--> <!-- not available on the central --> <groupId>com.jolira</groupId> <artifactId>onejar-maven-plugin</artifactId> <version>1.4.4</version> <executions> <execution> <configuration> <mainClass>${fully.qualified.class.name}</mainClass> <attachToBuild>true</attachToBuild> <!-- https://code.google.com/p/onejar-maven-plugin/issues/detail?id=8 --> <!--classifier>onejar</classifier--> <filename>${project.build.finalName}-onejar.${project.packaging}</filename> </configuration> <goals> <goal>one-jar</goal> </goals> </execution> </executions> </plugin>
$ ls -1 target/ ... executable-jar-with-maven-example-x.y.z-onejar.jar ... $ java -jar target/executable-jar-with-maven-example-x.y.z-onejar.jar ... $
Spring Boot Maven Plugin
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> <configuration> <classifier>spring-boot</classifier> <mainClass>${fully.qualified.main.class}</mainClass> </configuration> </execution> </executions> </plugin>
$ ls -1 target/ ... executable-jar-with-maven-example-x.y.z-spring-boot.jar ... $ java -jar target/executable-jar-with-maven-example-x.y.z-spring-boot.jar ... $
Creating an executable JAR file
References
Jar
public class HelloWorld { public static void main(final String[] args) { System.out.println("hello, world"); } }
$ javac HelloWorld.java $ java HelloWorld hello, world $ cat HelloWorld.mf Main-Class: HelloWorld $ jar cfm HelloWorld.jar HelloWorld.mf HelloWorld.class $ java -jar HelloWorld.jar hello, world $
Maven
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- The Basics --> <groupId>test</groupId> <artifactId>hello-world</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.imgscalr</groupId> <artifactId>imgscalr-lib</artifactId> <version>4.2</version> </dependency> </dependencies> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <!-- Build Settings --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.4</version> <configuration> <archive> <manifest> <mainClass>test.HelloWorld</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.4</version> <configuration> <archive> <manifest> <mainClass>test.HelloWorld</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <!-- More Project Information --> <name>hello-world</name> <url>http://maven.apache.org</url> <!-- Enrivonment Settings --> </project>
$ ls -R src/main/java/ src/main/java/: test/ src/main/java/test: HelloWorld.java* $ cat src/main/java/test/HelloWorld.java package test; public class HelloWorld { public static void main(final String[] args) { System.out.println( "hello, world"); } } $ mvn clean package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.613s [INFO] Finished at: Sat Apr 27 00:27:33 KST 2013 [INFO] Final Memory: 15M/223M [INFO] ------------------------------------------------------------------------ $ ls -l target/ archive-tmp/ classes/ hello-world-1.0-SNAPSHOT.jar* hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar* maven-archiver/ surefire/ $ jar tf target/hello-world-1.0-SNAPSHOT.jar META-INF/ META-INF/MANIFEST.MF test/ test/HelloWorld.class META-INF/maven/ META-INF/maven/test/ META-INF/maven/test/hello-world/ META-INF/maven/test/hello-world/pom.xml META-INF/maven/test/hello-world/pom.properties $ java -jar target/hello-world-1.0-SNAPSHOT.jar hello, world $ jar tf target/hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar META-INF/ META-INF/MANIFEST.MF org/ org/imgscalr/ org/imgscalr/AsyncScalr$1.class org/imgscalr/AsyncScalr$10.class org/imgscalr/AsyncScalr$11.class org/imgscalr/AsyncScalr$12.class org/imgscalr/AsyncScalr$13.class org/imgscalr/AsyncScalr$14.class org/imgscalr/AsyncScalr$2.class org/imgscalr/AsyncScalr$3.class org/imgscalr/AsyncScalr$4.class org/imgscalr/AsyncScalr$5.class org/imgscalr/AsyncScalr$6.class org/imgscalr/AsyncScalr$7.class org/imgscalr/AsyncScalr$8.class org/imgscalr/AsyncScalr$9.class org/imgscalr/AsyncScalr$DefaultThreadFactory.class org/imgscalr/AsyncScalr$ServerThreadFactory.class org/imgscalr/AsyncScalr.class org/imgscalr/Scalr$1.class org/imgscalr/Scalr$Method.class org/imgscalr/Scalr$Mode.class org/imgscalr/Scalr$Rotation.class org/imgscalr/Scalr.class META-INF/maven/ META-INF/maven/org.imgscalr/ META-INF/maven/org.imgscalr/imgscalr-lib/ META-INF/maven/org.imgscalr/imgscalr-lib/pom.xml META-INF/maven/org.imgscalr/imgscalr-lib/pom.properties test/ test/HelloWorld.class META-INF/maven/test/ META-INF/maven/test/hello-world/ META-INF/maven/test/hello-world/pom.xml META-INF/maven/test/hello-world/pom.properties $ java -jar target/hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar hello, world $