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?

Advertisements

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.MFClass-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 Pluginjar-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

$