Shinnara's Blog
Talking with Shinnara :: NaraTalk.com


 Java 로 Desktop 에서 돌아가는 어플리케이션을 개발을 하다보면 자주 접하는 문제 중에 하나가 배포를 어떻게 할까입니다. 특히 Windows 에서 어떻게 하면 사용자가 편리하게 실행시킬 수 있을까를 고민하게 됩니다.
가장 기본적인 것으로는 배치 파일을 만들어서 그 파일 안에 "java MainForm" 과 같은 실행 스크립트를 적는 것이겠지요. 물론 매우 열악한 해결방법이긴 하지만요..
 
좀더 나아간다면 JNI 를 이용해서 Wrapper 를 제작하는 방법입니다. 즉 해당 플랫폼에 맞는 실행화일을 만들어서 이 실행화일이 Java 프로그램을 띄우게 되는 구조입니다. Eclipse 와 같은 프로그램들이 이와 같은 방법을 사용합니다. 이 경우, 별도의 설치 없이 파일을 특정 디렉토리에 설치하는 것만으로 실행할 수 있습니다. 하지만 프로그램이 실행되는데 필요한 조건들이 선결되어야 한다는 가정이 있어야 겠지요. 예를 들어 JRE 가 설치되어 있지 않다면 Wrapper 가 Java VM 을 부르지 못하니까요.

한단계 더 나아간다면 인스톨러를 활용하는 것입니다. 일반적인 어플리케이션의 설치과정처럼 인스톨러가 있어서 환경에 대한 체크도 해주고, 실행 화일도 만들어주는 형태입니다. 사용자에게 가장 친숙하게 다가설 수 있는 방법입니다.

 그외에도 JNLP(java network launching protocol) 를 이용한 Java Web Start 도 대안이 될 수 있을 것입니다. 이는 네트워크를 통하여 어플리케이션을 설치하고 실행시키는 방법입니다.

위의 내용과 관련된 자세한 자료는 다음을 참고하세요. 기본적인 설명과 다양한 툴을 소개하고 있습니다.

http://www.excelsior-usa.com/articles/java-to-exe.html

오늘 소개할 내용은 위의 문서에도 나오는 Launch4j 입니다.


사용자 삽입 이미지
Launch4j 는 Wrapper 를 통하여 exe 를 만드는 방법입니다. 전에는 JSmooth 라는 툴을 잘 써왔는데, 이상하게도 JSmooth 에서는 classpath 를 제대로 찾지 못하는 문제가 종종 발생해서 관련된 클래스를 모두 하나의 jar 에 묶어 embed 하여 exe 를 만들어야 했습니다. 제가 잘 못해서 그런지는 모르겠지만 여하튼 그것때문에 예전에 배포한 한 프로그램은 실행화일의 크기가 4MB 가 넘어갔습니다.  그리고 수정이 있을때마다 패키징을 다시해야하는 문제까지 생겼습니다. 그래서 다른 툴을 알아보게 되었는데, 오늘 테스트해본 툴이 바로 Launch4j 입니다.

Launch4j 의 홈페이지에 보면 2.x와 3.x 의 두가지 버전을 다운로드 할 수 있게 하고 있는데, 3.x 를 추천합니다. stable 버젼은 2.x 이지만 , 받아서 설치해보니 Classpath 와 관련된 내용을 설정할 수 없도록 되어 있더군요. 3.x 에서는 제대로 동작합니다.

제가 설치한 버젼은 3.0.0-pre2 입니다. 제일 궁금한 것이 classpath 에 있는 라이브러리를 제대로 참조하여 실행 시킬수 있는가 하는 것입니다. 그래야 Wrapper 를 한번 만들면 그 다음부터는 관련된 클래스화일만 변경해도 프로그램이 변경될 수 있으니까요.

그래서 간단한 프로그램을 작성해보았습니다. JFrame 안에 두개의 JTextField 를 위 아래에 놓고, 위에는 현재 시간을, 아래에는 이 내용을 Base64 로 인코딩한 정보를 보여주는 간단한 어플리케이션입니다. Base64 를 쓰는 이유는 별도의 라이브러리인  commons-codec을 제대로 참조하는지 테스트하기 위함입니다.

소스 파일은 두개로 나누어보았습니다.

[Main.java]

package com.naratalk.test;
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
import java.util.*;


public class Main extends JFrame{

    JTextField tfDate,tfEncoded;
    UserLib ul;
    public Main()
    {
        super("Main Application");
        ul = new UserLib();
        initUI();
        addUpdateTask();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
   
    private void initUI()
    {
        this.setLayout(new BorderLayout());
        tfDate = new JTextField();       
        tfDate.setBorder(new TitledBorder("Current Date & Time"));       
       
        this.add(tfDate, BorderLayout.NORTH);       
       
        tfEncoded = new JTextField();       
        tfEncoded.setBorder(new TitledBorder("Encoded"));       
       
        this.add(tfEncoded, BorderLayout.SOUTH);
    }
   
    private void addUpdateTask()
    {
        java.util.Timer timer = new java.util.Timer();
        timer.schedule( new TimerTask() {
            public void run()
            {
                tfDate.setText(ul.getSimpleDate());
                tfEncoded.setText(ul.encodeBase64(tfDate.getText()));
                addUpdateTask();
            }
        }, 1000);
    }
   
   
    public static void main(String[] args)
    {
        Main mf = new Main();
        mf.setSize(500, 500);
        mf.setVisible(true);
    }
}


[UserLib.java]

package com.naratalk.test;

import java.text.*;
import java.util.*;
import org.apache.commons.codec.binary.*;
public class UserLib {

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   
    public String getSimpleDate()
    {
        return sdf.format(new Date());
    }
   
    public String encodeBase64(String str)
    {
        Base64 base64 = new Base64();
        return new String(base64.encode(str.getBytes()));       
    }
}


그리고 Ant 의 build 파일은 전에 설명한 것처럼 dist 디렉토리에 jar 로 만들어놓습니다.

[build.xml]

<project name="InstallerTest" default="main" basedir=".">
    <target name="init">
        <property name="build" value="bin" />
        <property name="home" value="." />
        <property name="src" value="src" />
        <property name="dist" value="dist" />
        <property name="jarname" value="installertest.jar" />
        <property name="mainclass" value="com.naratalk.test.Main" />
    </target>

    <target name="compile" depends="init">
        <mkdir dir="${build}" />
        <javac srcdir="${src}"  destdir="${build}" >
            <classpath>
                    <fileset dir="lib">
                        <include name="**/*.jar" />
                    </fileset>
                </classpath>
       </javac>
    </target>

    <target name="makejar" depends="compile">
        <mkdir dir="${dist}" />
        <jar destfile="${dist}/${jarname}"
             basedir="${build}" >
             <manifest>
                <attribute name="Main-Class" value="${mainclass}" />
             </manifest>
        </jar>
    </target>

    <target name="main" depends="compile" />
   
</project>

그리고 dist 디렉토리에 이번에 쓰인 라이브러리인 commons-codec-1.3.jar 를 옮겨 놓습니다. 빌드를 수행하고나면 dist 디렉토리에 installertest.jar 와 commons-codec-1.3.jar 가 있게 됩니다.

이제 Launch4j 를 사용할 차례입니다.

뭐 특별한 내용은 없습니다. 워낙 심플한 것이라서요. 이번 글에서 테스트하고 싶은게 결국 classpath 를 제대로 인식하느냐의 문제이므로 이부분에 대해 적어보겠습니다.

사용자 삽입 이미지

Classpath 탭에서 Custom classpath 를 체크하고,  MainClass 에는 폴더버튼을 눌러 main class 가 있는 jar 를 선택하고, Edit Item 에 commons-codec-1.3.jar 라고 입력하고 Accept 버튼을 누르면 Classpath 에 추가가 됩니다.


사용자 삽입 이미지

그리고 JRE 탭에서는 Minimun JRE 버젼은 1.x.x 형태로 입력하시면 됩니다. 저의 경우는 Don't user private JREs 를 선택했습니다.

사용자 삽입 이미지
이렇게 하고 톱니모양의 버튼을 누르면 exe 파일이 생성되는데, 제 경우는 24KB 정도되는 크기입니다. 이 exe 파일은 Main-Class 를 불러주는 역할만을 하므로 Main-Class 의 이름이 바뀌지 않는다면 프로그램이 변경되더라도 exe 자체를 변경할 필요가 없습니다. 물론 classpath 에 추가할 내용이 있다면 다시 해야겠지만요. 오른쪽의 이미지는 이 프로그램을 실행한 화면입니다.



Launch4j 는 Ant 를 이용해서 작업을 진행할 수도 있습니다. 이와 관련해서는 다음번에 다시 한번 글을 올리도록하겠습니다.






0 Trackback, 1 Comment

TRACKBACK :: http://naratalk.com/trackback/159 관련글 쓰기

댓글을 달아 주세요

  1. 박찬식  댓글주소  수정/삭제  댓글쓰기

    정말 감사합니다.. JSmooth로 하다가 도저히 외부 jar 클래스패스 설정이 안되서 오늘 8시간 해매다 이 포스트 보고 해결했습니다.ㅜㅜ
    정말 눈물이 앞을 가리네요...
    버그였다니.. 그것도 모르고 구글링만 몇시간을 한건지..

    2010/09/28 17:33

1  ... 162 163 164 165 166 167 168 169 170  ... 264 
다...... (264)
Computer/Programming (106)
Links (14)
책 읽는 즐거움 (7)
끄적임 (66)
즐거운 과학 나라 (7)
일본 (5)
Study (4)