JNLP 클라이언트

본 장에서는 클라이언트와 이에 대한 JNLP 파일을 웹 애플리케이션에 deploy하는 방법과 실제로 사용자가 클라이언트를 실행하는 방법에 대해 설명한다.

1. 개요

애플리케이션의 사이즈가 크고 자주 변경이 된다면, 소프트웨어 컴퍼넌트의 배포 및 실행에 관한 프로토콜인 JNLP(Java Network Launching Protocol)을 사용한다. 프로토콜을 사용하여 클라이언트 애플리케이션(이하 클라이언트)을 웹 서버를 통해 자동으로 다운받아 클라이언트를 실행할 수 있는데 이를 Java Web Start라고 한다. JNLP를 통해 다운받은 클라이언트도 다른 것들과 마찬가지로 JEUS를 사용할 수 있다.

JNLP를 사용하면 클라이언트 프로그램의 배포가 간편하다. 버전이 업그레이드되면 자동으로 필요한 바이너리를 전송받으므로 지속적인 배포가 가능하다. 또한 JDK 홈 디렉터리의 sample\jnlp에는 여러 가지 JNLP 샘플이 있으므로 이 디렉터리 아래에 위치한 README 파일을 따라 실행하면 JNLP를 쉽게 연습할 수 있다.

본 안내서에서 JNLP에 대한 상세한 정보를 제공하지 않는다. 그러므로 JNLP에 대해서 어느 정도의 사전지식이 필요하다. JLNP를 통해서 JAR 파일을 배포할 때는 Java Permission 문제가 항상 따라다니기 때문에 대부분 sign을 하게 된다. JEUS에서 제공하는 jclient.jar, clientcontainer.jar 등의 파일들은 sign이 되어 있지 않으므로 반드시 다른 JAR 파일들과 함께 sign한다. JNLP의 자세한 내용은 http://docs.oracle.com/javase/7/docs/technotes/guides/javaws를 참고한다.

2. 프로그램 작성

본 절에서는 웹 애플리케이션의 구성 방법에 대해 설명한다.

2.1. 프로그램 구성

JNLP 파일을 웹에서 얻고 파일에 정의된 JAR 파일들을 웹 서버에서 다운받으려면 JNLP 프로토콜을 구현한 서블릿이 필요하다. JEUS에서는 이를 직접 제공하지 않으므로 JDK 8에서 제공하는 샘플 JNLP 서블릿을 사용한다.

서블릿을 구현한 jnlp-servlet.jar 파일은 JDK 8 기준으로 다음의 디렉터리에 위치한다(IBM JDK 8 lib 디렉터리).

sample/jnlp/servlet

jnlp-servlet.jar 파일을 웹 애플리케이션의 WEB-INF\lib에 복사해두고 다음과 같이 web.xml을 작성하면 서블릿이 JNLP 프로토콜에 따라 JNLP 파일과 리소스 파일들을 클라이언트로 전송한다.

JNLP 프로토콜에 따라 리소스 파일을 전송하는 'JnlpDownloadServlet’에 대한 자세한 설명은 https://docs.oracle.com/javase/8/docs/technotes/guides/javaws/developersguide/downloadservletguide.html를 참고한다.

2.2. 예제

다음은 JNLP 서블릿을 위한 web.xml 예제이다.

JNLP 서블릿 : <web.xml>
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
       <servlet-name>JnlpDownloadServlet</servlet-name>
       <servlet-class>jnlp.sample.servlet.JnlpDownloadServlet</servlet-class>
       <init-param>
           <param-name>logLevel</param-name>
           <param-value>DEBUG</param-value>
       </init-param>
       <init-param>
           <param-name>logPath</param-name>
           <param-value>jnlpdownloadservlet.log</param-value>
       </init-param>
  </servlet>
  <servlet-mapping>
      <servlet-name>JnlpDownloadServlet</servlet-name>
      <url-pattern>*.jnlp</url-pattern>
  </servlet-mapping>
</web-app>

다음은 HelloClient 클래스의 소스이다. JNLP 클라이언트는 일반 클라이언트 동일하다. JNLP 클라이언트는 콘솔 화면이 없으므로 여기서는 애플릿과 같이 Swing을 사용하여 GUI 화면을 만들었다.

JNLP 클라이언트 : <HelloClient.java>
package helloejb;

import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.*;

import javax.swing.*;

public class HelloClient extends JFrame {
   public static void main(String[] args)
   {
      new HelloClient();
   }

   public HelloClient() {
    try {
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY,"jeus.jndi.JNSContextFactory");
            Context context = new InitialContext(env);
            Hello hello = (Hello) context.lookup("helloejb.Hello");

            JLabel label = new JLabel(hello.sayHello());
            label.setFont(new Font("Helevetica", Font.BOLD, 15));
            getContentPane().setLayout(new BorderLayout());
            getContentPane().add(label, BorderLayout.CENTER);
            setSize(500, 250);
            setVisible(true);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
   }
}

Java Web Start는 HelloClient를 실행하기 위한 정보가 담긴 JNLP 파일을 요청한다.

다음의 예에서는 HelloClient를 수행하기 위해 필요한 JAR 파일과 사용하는 Java 버전 등이 명시되어 있다. <jnlp> 태그에서 codebase 속성의 '$$codebase’는 JNLP 서블릿을 포함한 웹 애플리케이션이 deploy되는 컨텍스트에 따라 자동으로 교체된다. 여기서는 JAR 파일의 href 값에 별다른 경로가 지정되어 있지 않으므로 JNLP 파일과 JAR 파일은 모두 동일한 디렉터리에 위치시켜야 한다.

JNLP 클라이언트 : <HelloClient.jnlp>
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0" codebase="$$codebase">
   <information>
      <title>HelloClient</title>
      <vendor>TmaxSoft</vendor>
   </information>
   <resources>
      <j2se version="1.5+" href="http://java.sun.com/products/autodl/j2se"/>
      <jar href="hello-client.jar"/>
      <jar href="jclient.jar"/>
   </resources>
   <application-desc main-class="helloejb.HelloClient"/>
</jnlp>

'$$codebase’와 같은 예약어는 Oracle의 JNLP 서블릿 구현에서 제공하는 것이며 JNLP 표준에서 정의한 것은 아니다.

3. 프로그램 실행

본 절에서는 클라이언트의 실행 방법에 대해 설명한다.

3.1. 클라이언트 실행

웹 애플리케이션을 deploy한 후 JNLP 파일을 웹 브라우저에서 접근하면 Java Web Start가 시작되고 클라이언트가 실행된다.

다음은 웹 애플리케이션의 컨텍스트가 hello인 경우에 JNLP 파일을 요청하는 URL의 예이다. HelloClient.jnlp와 HelloClient.jnlp에 정의된 hello-client.jar, jclient.jar는 url path가 app로 되어 있기 때문에 hello 디렉터리 혹은 WAR 파일 내의 app 디렉터리에 위치한다.

http://host1:8088/hello/app/HelloClient.jnlp

이 JNLP 파일을 웹 브라우저에서 요청한 결과로 지정된 클라이언트가 수행된다. 웹 브라우저와 별도의 Java 화면이 나타나고 클라이언트가 실행된 것을 확인할 수 있다.

3.2. 클라이언트 컨테이너에서 실행

JNLP 클라이언트의 경우 클라이언트 컨테이너를 통해서 Dependency Injection을 사용할 수 있다. 그렇게 하려면 위에서 제시한 방법과는 다른 방법이 필요하다.

다음은 HelloClient.java에서 Injection을 사용한 코드의 예이다.

클라이언트 컨테이너 실행 : <HelloClient.java>
package helloejb;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.*;

import javax.swing.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.ejb.EJB;

public class HelloClient extends JFrame {
   @EJB
   private static Hello hello;

   public static void main(String[] args) {
      new HelloClient();
   }

   public HelloClient() {
    try {
            JLabel label = new JLabel(hello.sayHello());
            label.setFont(new Font("Helevetica", Font.BOLD, 15));
            getContentPane().setLayout(new BorderLayout());
            getContentPane().add(label, BorderLayout.CENTER);
            setSize(500, 250);
            setVisible(true);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
   }
}

JNLP 파일도 이에 맞추어서 작성해야 한다. 클라이언트의 메인 클래스 대신에 JEUS의 클라이언트 컨테이너 클래스를 기술하고 그 클래스에 전달할 파라미터를 설정해야 한다. 파라미터에 대한 설명은 콘솔에서 실행을 참고한다.

<resources> 태그에는 client.jar 대신 clientcontainer.jar를 기술하고, JEUS 클라이언트 컨테이너가 Java Web Start로 실행하는 모드임을 알 수 있도록 jeus.client.container.jws 프로퍼티를 true로 설정해야 한다.

또한 Java Web Start 프로그램의 경우 Permission을 체크하기 때문에 <security> 태그에 <all-permissions> 또는 <j2ee-application-client-permissions>를 기술하고 JAR 파일은 반드시 sign을 해야 한다.

클라이언트 컨테이너 실행 : <HelloClient.jnlp>
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0" codebase="$$codebase">
   <information>
      <title>HelloClient</title>
      <vendor>TmaxSoft</vendor>
   </information>
   <security>
      <all-permissions/>
   </security>
   <resources>
      <j2se version="1.5+" href="http://java.sun.com/products/autodl/j2se"/>
      <jar href="clientcontainer.jar"/>
      <jar href="hello-client.jar"/>
      <property name="jeus.client.container.jws" value="true"/>
   </resources>
   <application-desc main-class="jeus.client.container.ClientContainer">
        <argument>-main</argument>
        <argument>helloejb.HelloClient</argument>
   </application-desc>
</jnlp>

위의 경우와 마찬가지로 웹 브라우저나 Java Web Start를 통해서 JNLP 파일을 로딩하면 실행되는 결과는 동일하다.

JAR 파일 sign하기

JNLP 파일에 <security> 태그가 있을 경우 Java Web Start는 반드시 signed JAR 파일을 요구하기 때문에 예제의 hello-client.jar를 sign해야 한다.

다음은 JDK에서 제공하는 keytool과 jarsigner를 사용해서 hello-client.jar를 sign하는 예이다.

keytool -genkey -alias helloclient -keypass 1234 -keystore helloks -storepass 1234
jarsigner -keystore helloks hello-client.jar helloclient