반응형
JNI 콜백 메소드 만들때도 참조하시면 좋습니다.
 
원문: http://www.velocityreviews.com/forums/t360927-setwindowshookex-not-notifing-me-on-key-pressed-using-jni-and-c-dll.html
 
Here is a very simple yet complete working sample of a
low level Windows keyboard hook within a Swing application.
Please try it.

//
// FrameTest.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class FrameTest extends JFrame {
private JPanel mainPanel;
private JTextArea mainTextArea;
private HookTest hook;

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new FrameTest().setVisible(true);
}
});
}

FrameTest() {
super("FrameTest");
setSize(200, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainTextArea = new JTextArea();
mainPanel.add(mainTextArea, BorderLayout.CENTER);
getContentPane().add(mainPanel);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
hook.unRegisterHook();
}
});
new Thread() {
public void run() {
hook = new HookTest();
hook.registerHook();
}
}.start();
}
}

//
// HookTest.java
//
public class HookTest {
static {
System.loadLibrary("HookTest");
}

void processKey(int key, boolean pressed) {
System.out.println("Java: HookTest.processKey - key = " + key +
(pressed ? " pressed" : " released"));
}

native void registerHook();
native void unRegisterHook();
}

//
// HookTest.h
//
#ifndef _Included_HookTest
#define _Included_HookTest

#include <jni.h>

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT void JNICALL Java_HookTest_registerHook(JNIEnv * env, jobject
obj);

JNIEXPORT void JNICALL Java_HookTest_unRegisterHook(JNIEnv * env,
jobject obj);

#ifdef __cplusplus
}
#endif

#endif /* _Included_HookTest */

//
// HookTest.cpp
//
#include <windows.h>
#include "HookTest.h"

HINSTANCE hInst = NULL;
JavaVM * jvm = NULL;
jobject hookObj = NULL;
jmethodID processKeyID = NULL;
DWORD hookThreadId = 0;

extern "C" BOOL APIENTRY DllMain(HINSTANCE _hInst, DWORD reason, LPVOID
reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
printf("C++: DllMain - DLL_PROCESS_ATTACH.\n");
hInst = _hInst;
break;
default:
break;
}

return TRUE;
}

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM
lParam) {
JNIEnv * env;
KBDLLHOOKSTRUCT * p = (KBDLLHOOKSTRUCT *)lParam;

if (jvm->AttachCurrentThread((void **)&env, NULL) >= 0) {
switch (wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
printf("C++: LowLevelKeyboardProc - Key pressed\n");
env->CallVoidMethod(hookObj, processKeyID, p->vkCode,
true);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
printf("C++: LowLevelKeyboardProc - Key released\n");
env->CallVoidMethod(hookObj, processKeyID, p->vkCode,
false);
break;
default:
break;
}
}
else {
printf("C++: LowLevelKeyboardProc - Error on the attach current
thread.\n");
}

return CallNextHookEx(NULL, nCode, wParam, lParam);
}

void MsgLoop() {
MSG message;

while (GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}

JNIEXPORT void JNICALL Java_HookTest_registerHook(JNIEnv * env, jobject
obj) {
HHOOK hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,
LowLevelKeyboardProc, hInst, 0);

if (hookHandle == NULL) {
printf("C++: Java_HookTest_registerHook - Hook failed!\n");
return;
}
else {
printf("C++: Java_HookTest_registerHook - Hook successful\n");
}

hookObj = env->NewGlobalRef(obj);
jclass cls = env->GetObjectClass(hookObj);
processKeyID = env->GetMethodID(cls, "processKey", "(IZ)V");
env->GetJavaVM(&jvm);
hookThreadId = GetCurrentThreadId();

MsgLoop();

if (!UnhookWindowsHookEx(hookHandle))
printf("C++: Java_HookTest_registerHook - Unhook failed\n");

else
printf("C++: Java_HookTest_registerHook - Unhook
successful\n");
}

JNIEXPORT void JNICALL Java_HookTest_unRegisterHook(JNIEnv *env,
jobject object) {
if (hookThreadId == 0)
return;

printf("C++: Java_HookTest_unRegisterHook - call
PostThreadMessage.\n");
PostThreadMessage(hookThreadId, WM_QUIT, 0, 0L);
}


Regards
반응형

Access Windows Registry using 'pure' Java
(rehash of my old post since all those wonderful blog post drafts I was planning to publish are unfortunately out-dated or irrelevant)

One can use the private code of Sun's Preferences API to access values of type REG_SZ in the Windows Registry. Stupid hack. Maybe I should make a library out of this. I have used this soooo many times.

The java.util.prefs.WindowsPreferences is the concrete implementation of AbstractPreferences in the Windows platform. This class provides methods like WindowsRegQueryValueEx, etc. Using Reflection, one can use the methods in this class to query string values under HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER


Given below is a code-snippet that demonstrates how to get the ProxyServer setting on Windows (this is what IE uses/sets) and the Internet Explorer version


import java.lang.reflect.Method;
import java.util.prefs.Preferences;

public class JavaRegistryHack {

private static final int HKEY_CURRENT_USER = 0x80000001;
private static final int KEY_QUERY_VALUE = 1;
private static final int KEY_SET_VALUE = 2;
private static final int KEY_READ = 0x20019;

public static void main(String args[]) {
final Preferences userRoot = Preferences.userRoot();
final Preferences systemRoot = Preferences.systemRoot();
final Class clz = userRoot.getClass();
try {
final Method openKey = clz.getDeclaredMethod("openKey",
byte[].class, int.class, int.class);
openKey.setAccessible(true);

final Method closeKey = clz.getDeclaredMethod("closeKey",
int.class);
closeKey.setAccessible(true);

final Method winRegQueryValue = clz.getDeclaredMethod(
"WindowsRegQueryValueEx", int.class, byte[].class);
winRegQueryValue.setAccessible(true);
final Method winRegEnumValue = clz.getDeclaredMethod(
"WindowsRegEnumValue1", int.class, int.class, int.class);
winRegEnumValue.setAccessible(true);
final Method winRegQueryInfo = clz.getDeclaredMethod(
"WindowsRegQueryInfoKey1", int.class);
winRegQueryInfo.setAccessible(true);


byte[] valb = null;
String vals = null;
String key = null;
Integer handle = -1;

//Query Internet Settings for Proxy
key = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
handle = (Integer) openKey.invoke(userRoot, toCstr(key), KEY_READ, KEY_READ);
valb = (byte[]) winRegQueryValue.invoke(userRoot, handle.intValue(),
toCstr("ProxyServer"));
vals = (valb != null ? new String(valb).trim() : null);
System.out.println("Proxy Server = " + vals);
closeKey.invoke(Preferences.userRoot(), handle);

// Query for IE version
key = "SOFTWARE\\Microsoft\\Internet Explorer";
handle = (Integer) openKey.invoke(systemRoot, toCstr(key), KEY_READ, KEY_READ);
valb = (byte[]) winRegQueryValue.invoke(systemRoot, handle, toCstr("Version"));
vals = (valb != null ? new String(valb).trim() : null);
System.out.println("Internet Explorer Version = " + vals);
closeKey.invoke(Preferences.systemRoot(), handle);

} catch (Exception e) {
e.printStackTrace();
}
}


private static byte[] toCstr(String str) {
byte[] result = new byte[str.length() + 1];
for (int i = 0; i < str.length(); i++) {
result[i] = (byte) str.charAt(i);
}
result[str.length()] = 0;
return result;
 }
}










<출처 : http://lenkite.blogspot.com/2008/05/access-windows-registry-using-java.html >
반응형
자바로 개인 프로젝트를 하다가 윈도우의 레지스트리에 접근하려고 자료를 찾아봤습니다.
일반적으로 시스템쪽을 컨트롤 하려면 JNI를 사용해야 되는것 같더군요.
일단 레지스트리 컨트롤 할 수 있는 라이브러리 관련 자료 정리해 봅니다.(JNI를 이용하여)

http://www.bayequities.com/tech/projects.shtml  <-- 사이트가 사라진듯..안되는군요 ^^;
사용자 삽입 이미지

jRegistryKey is a Java™ Native Interface (JNI) wrapper around the Microsoft® Windows® Win32® application programming interface (API) registry functions, designed to facilitate Windows® registry access for Java™ developers. jRegistry Key User Manual

ClassPath에 포함시킬 파일들 입니다.

invalid-file

jRegistryKey.dll





예제소스입니다.

import ca.beq.util.win32.registry.*;

public class AppMain
{
    public AppMain()
    {
        super();
    }

    public static void main(String[] args)
    {
        RegistryKey r = new RegistryKey(RootKey.HKEY_LOCAL_MACHINE, "Software\\Ncsoft\\L2Client");
       
        String key = "workingdir";
        if(r.hasValue(key))
        {
           RegistryValue v = r.getValue(key);
           String value = v.toString();
           value = value.replaceAll(v.getName()+":", "").replaceAll("REG_SZ:", "").replaceAll("REG_DWORD:", "");
           System.out.println("lineage2 working dir : " + value);
   
           RegistryValue vs = r.getValue("global_version");
           System.out.println(vs.toString());
        }
        else
            System.out.println("Not Existed Key");
    }
}


레지스트리 편집기를 이용하여 값과 결과 화면을 확인해 보세요.^^
--------------------------------------------------------------------------------------------------------------------------------------
밑에 자료는 다른 패키지를 이용하는 방법이다.

Windows Registry API Native Interface

Release 3.1.3, September 11, 2003

The com.ice.jni.registry package is a Java native interface for the Windows Registry API. This allows Java program to access, modify, and export Windows Registry resources.

The com.ice.jni.registry package has been placed into the public domain. Thus, you have absolutely no licensing issues to consider. You may do anything you wish with the code. Of course, I always appreciate it when you properly credit my work.

The package will work only with Java 1.1 and greater, and uses the Javasoft native interface, not the Netscape interface. The package also includes a DLL that implements the interface. The package has been used with JDK1.2, and JDK1.3, JDK1.4, as well as JDK1.1.8.

The package includes the pre-built DLL (debug and release), source code (both the Java and the DLL's C code), as well as the compiled Java classes.

The original release was posted on November 17, 1997. The current release is 3.1.3, which was posted on September 11, 2003.

http://www.trustice.com/java/jnireg/index.shtml   <-- 여기에서 다운받으면 된다.

--------------------------------------------------------------------------------------------------------------------------------------

http://sourceforge.net/projects/java-registry/   <-- 이것도 다른 패키지인듯...

반응형
자바웹스타트 응용 시스템을 만들다 보면 때로는 클레스와 함께 jar 화일에 묶여있는 이미지 화일이나 프로퍼티 화일, 자료 화일 등이 필요할 경우가 있다.  이러한 경우에는 java.lang.ClassLoader로 부터 해당 자원을 얻을 수 있다. 

다음은 jar화일에 묶여있는 이미지 화일로부터 Icon 객체를 생성해내는 예제이다.

ClassLoader loader = this.getClass().getClassLoader();
Icon myIcon = new ImageIcon(loader.getResource("imgs/myImg.gif"));

이 밖에도 java.lang.ClassLoader의 getResourceAsStream(String name) 메소드를 이용하면 다양한 자원을 InputStream을 통하여 얻을 수 있다.

  
  자바웹스타트 환경에서는 Java 2 SE API에서 제공하지 않는 추가적인 기능을 하는 API를 제공한다. 이것은 JNLP API라고 한다. JNLP API를 이용하여 개발할 경우는 jnlp.jar가 필요한데 이러한 파일은 JNLP Developer's Pack에 포함 되어 있다. 다음은 Developer's Pack을 다운로드 할 수 있는 URL이다.

http://java.sun.com/products/javawebstart/download-jnlp.html

  JNLP API가 추가적으로 제공하는 클레스는 javax.jnlp package로 묶여 있으며 BasicService, ClipboardService, DownloadService, FileOpenService, FileSaveService,  PrintService, PersistenceService 등이 있는데 이들은 ServiceManager 클레스를 통하여 사용할 수 있다. 각각의 기능은 다음과 같다.



- javax.jnlp.BasicService
BasicService는 웹스타트의 환경적인 면이나 브라우져를 통제하기 위한 API를 제공하는데 자바 애플릿의 경우 AppletContext와 비슷한 역할을 한다. 다음 예제는 웹스타트 환경에서 웹브라우져로하여금 특정 URL로 가도록 하는 것이다.

import javax.jnlp.*;
.....

BasicService bs = (BasicService)ServiceManager.lookup("javax.jnlp.BasicService");
bs.showDocument(new URL("http://www.javanuri.com"));

- javax.jnlp.ClipboardService
ClipboardService는 시스템에서 사용하는 클립보드에서 복사 객체를 가져오거나 클립보드로 복사하는 서비스를 제공한다. 자바웹스타트는 이 기능을 사용할 때 보안을 위하여 경고창을 보여준다. 다음은 간단한 스트링을 클립보드에 복사하는 예제이다.

import javax.jnlp.*;
.............

ClipboardService cs = (ClipboardService)ServiceManager.lookup("javax.jnlp.ClipboardService");
StringSelection ss = new StringSelection("Hello Web Start");
cs.setContents(ss);

- javax.jnlp.DownloadService
DownloadService는 자신의 자원을 Cache에 저장, 삭제등 Cache를 통제할 수 있는 서비스 API를 제공하는 클레스이다. 다음은 myapp.jar를 Cache에서 확인하고 있으면 삭제한후 다시 Cache에 저장하는 예제이다.

import javax.jnlp.*;
...........

DownloadServicd ds = (DownloadService)ServiceManager.lookup("javax.jnlp.DownloadService");
URL url = new URL("http://www.javanuri.com/jws/myapp.jar");
boolean isCached = ds.isResourceCached(url, "1.0");
if(isCached) {
ds.removeResource(url, "1.0");
}

DownloadServiceListener dsl = ds.getDefaultProgressWindow();
ds.loadResource(url, "1.0", dsl);

- javax.jnlp.FileOpenService
FileOpenService는 권한이 제약된 환경에서도 이를 사용자에게 알리고 화일을 열 수 있는 다이얼로그 윈도우를 열어주는 서비스이다.  다음 예제는 FileOpenService를 이용하여 화일을 여는 예제이다.

import javax.jnlp.*;
..............

FileOpenService fo = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService");
FileContents fc = fo.openFileDialog(null, null);

- javax.jnlp.FileSaveService
FileSaveService는 권한이 제약된 환경에서도 local disk에 화일을 저장할 수 있는 기능을 제공하는 서비스 클레스이다. 이는 FileOpenService의 경우와 반대인 기능을 제공하는 클레스이다.  다음은 FileOpenService를 이용하여 화일을 연 후에 FileSaveService를 이용하여 화일을 저장하는 예제이다.

import javax.jnlp.*;
...................
..

FileOpenService fo = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService");
FileContents fc = fo.openFileDialog(null, null);
FileContents newfc =
fss.saveFileDialog(null, null, fc.getInputStream(), "newfile.txt");

- javax.jnlp.PrintService

PrintService는 권한이 제약된 웹스타트 환경에서도 프린트를 가능하게 해주는 API 를 갖고 있는 서비스 클레스이다.  이 API를 이용하여 프린트를 요청하면 사용자에게 허가할 것인가를 묻는 다이얼로그가 나타난다. 다음은 PrintService를 이용한 프린트 요청 예제이다.

import javax.jnlp.*;
.....................

PrintService ps = (PrintService)ServiceManager.lookup("javax.jnlp.PrintService");

// default page format
PageFormat pf = ps.getDefaultPage();

// customizing page format
PageFormat npf = ps.showPageFormatDialog(pf);

// print
ps.print(new Doc());


// printable class
class Doc implements Printable {
....
public int print(Graphics g, PageFormat fm, int idx) {
....
}
}
}

- javax.jnlp.PersistenceService

PersistenceService는 브라우져의 쿠키와 마찬가지고 사용자의 클라이언트에 간단한 자료를 저장할 때 사용된다. 저장되는 형태는 url형태로 자장된다.
다음은 간단한 url을 저장하고 내용을 읽어들이는 예제이다.

import javax.jnlp.*;
.....................

PersistenceService ps = (PersistenceService)ServiceManager.lookup("javax.jnlp.PersistenceService");

String addr = "www.javanuri.com/some.txt";
java.net.URL = new URL(addr);

// create
ps.create(url, 1024);
FileContents fc = ps.get(url);
OutputStream os = fc.getOutputStream(false);
os.write(...);

// read
fc = ps.get(url);
InputStream in = fc.getInputStream();

in.read(...);

.......

- javax.jnlp.FileContents

FileContents 는 FileOpenService, FileSaveService, PersistenceService와 같은 서비스에서  input과 output을 처리할 수 있도록 만들어진 클레스이다. 일반적인 File 클레스와 비슷하게 생각하면 된다.  보안과 자료 저장 형태 등이 일반 File 클레스와는 다르다.
반응형

1. JNI (Java Native Interface) 란 ?

- 자바가 다른 언어로 만들어진 어플리케이션과 상호 작용할 수 있는 인터페이스를 제공한다.

- 자바가상머신(JVM)이 원시 메소드(native method)를 적재(locate)하고 수행(invoke)할 수 있도록 한다

- JNI가 자바가상머신내에 포함됨으로써, 자바가상머신이 호스트 운영체제상의 입출력, 그래픽스, 네트워킹, 그리고 스레드와 같은 기능들을 작동하기 위한 로컬시스템호출(local system calls)을 수행할 수 있도록 한다.

* 쉽게 말해 Java와 다른 언어를 연동하는 솔루션입니다.

[그림1] C로 만들어진 Library와 JAVA를 연결해주는 JNI

2. Why do you need JNI ?

자바 네이티브 메쏘드(Java Native method, 이하 JNI)는 다른 언어로 작성된 코드를 자바에서 호출하도록 만들어진 규약이다. 현재는 C/C++에 대한 호출만을 정확하게 지원한다. 어떻게 보면 JNI는 자바가 만들어진 철학과 정반대되는 것이다.

그러나. Java에도 한계가 있다.

1. 속도 문제가 있는 계산 루틴
 > 자바가 Native Code(플랫폼에 종속적인 기계어 코드)에 비해 느리다.

2. 자바에서 하드웨어 제어

3. 자바에서 지원되지 않은 특정 운영체제 서비스
 > 자바의 클래스 라이브러리는 방대하고 다양한 서비스를 제공하지만, 특정 플랫폼에서 제공하는 고유의 서비스의 기능을 모두 포함할 수는 없다. 특히, 특수한 목적으로 제작된 하드웨어를 자바에서 제어해야 할 필요가 있다고 한다면, 자바만으로 해결하기는 힘들다.

4. 기존의 프로그램에서 자바가 제공하는 서비스를 이용
 > 기존에 작성된 프로그램이나 기존의 시스템(legacy)과의 연계 문제

∴ JNI를 써서 해결해보자.

3. C를 이용한 JNI 예제

VC++을 이용해 C문법으로 작성되어 만들어진 DLL을 로딩하여 Java에서 사용해보겠습니다.

1단계 : Native Method를 선언하는 자바 클래스 작성
2단계 : 1단계에서 작성한 클래스 컴파일
3단계 : javah를 사용해서 Native Method가 사용할 헤더 파일 생성
4단계 : C언어로 Native Method 실제 구현
5단계 : C 코드와 헤더 파일을 컴파일
6단계 : 자바 프로그램 실행

1단계 : Native Method를 선언하는 자바 클래스 작성

Java 소스 파일 : HelloJni_Jsource.java

import java.util.*;

class HelloJniClass {
   native void Hello();

  static {  System.loadLibrary("Hello_DLL");   }

  public static void main(String args[]) {   
      HelloJniClass myJNI=new HelloJniClass();
      myJNI.Hello();
   }
}

// 아래는 좀 위의 내용 보충 그림



2단계 : 1단계에서 작성한 클래스 컴파일



* 컴파일시에는 일반 java 컴파일때와 마찬가지로 환경변수 셋팅이 되어 있어야 합니다.
 -> Path가 JDK의 Javac.exe가 있는 폴더에 설정되어 있어야 합니다.

3단계 : javah를 사용해서 Native Method가 사용할 헤더 파일 생성

<참고: eclipse 사용시 컴파일된 클래스 파일 위치에서 javah 실행. 패키지 최상위에서
          패키지명까지 명시    ex) c:\>javah com.pmguda.HelloJniClass                 >

HelloJniClass.h을 열어보면

JNIEXPORT void JNICALL Java_HelloJniClass_Hello  (JNIEnv *, jobject);
위의 함수를 Implement만 해서 DLL을 만들면 됩니다. (4단계)


4단계 : C언어로 Native Method 실제 구현(1)

1) VC++ 프로젝트 만들기 : Win32용 DLL 프로젝트로 만듭니다.
New - Projects : Win32 Dynamic-Link Library

2) Add Files Projects : HelloJniClass.h 파일 추가

3) Projects Setings(Alt+F7)
   - Link탭에 Output file Name : 1단계의 2. 라이브러리 적재시 작성한 DLL파일명(Hello_DLL.dll)
   - C/C++탭 Preprocessor 카테고리의 Additional Include directories
       JDK의 Include폴더와 Include폴더 밑의 win32폴더

          예) C:\Program Files\Java\jdk1.5.0_03\include\,
             C:\Program Files\Java\jdk1.5.0_03\include\win32

4. 값의 전달과 리턴

1단계 : Java 소스 파일 StringPass_Jsource.java
* 일반 자바 메쏘드 선언과 동일합니다.

class JNI_Message {
   native byte[] Message(String input);

  // 라이브러리 적재(Load the library) 

  static {
    System.loadLibrary("Msg_DLL");
  }


  public static void main(String args[]) {   
 byte buf[];

    // 클래스 인스턴스 생성(Create class instance)
    JNI_Message myJNI=new JNI_Message();

    // 원시 메소드에 값을 주고 받음
    buf = myJNI.Message("Apple");
 
 System.out.print(buf); // 받은값 출력
 }
}

2단계 : 컴파일
 javac StringPass_Jsource.java

3단계 : header파일 생성
 javah JNI_Message

4단계 : method구현 : StringJNIDLLSource.c

#include <stdio.h>
#include <jni.h>
#include <string.h>
#include "JNI_Message.h"

JNIEXPORT jbyteArray JNICALL Java_JNI_1Message_Message (JNIEnv * env, jobject jobj, jstring input)
{
    jbyteArray jb;
    jboolean iscopy;
    char* buf;
    static char outputbuf[20];

    buf=(*env)->GetStringUTFChars(env, input, &iscopy);  // 입력 String 읽어오는 함수
    printf ("\nDLL receive Data from JAVA : %s\n",buf);   // 입력받은 내용을 출력
    strcpy(outputbuf,"Delicious !!\n");
    jb=(*env)->NewStringUTF(env, outputbuf);  // 출력할 내용의 java버퍼에 output버퍼값을 셋팅

   return(jb); // java버퍼 리턴
}

(*env)->함수명 형태로, JAVA의 메쏘드를 C에서 이용할수 있습니다.
* JAVA는 C로 문자열을 넘겨줄때 UTF-8형태를 사용합니다.

5단계 : 실행

C:\test\C_JNI\Paramerter Pass>java JNI_Message

DLL receive Data from JAVA : Apple
Delicious !!

5. KVM ? KNI ?

KVM은 J2ME의 일부로서 작고 자원이 한정된 기계장치를 위해 설계된 소형 JVM.
JVM에서는 JNI가 KVM의 KNI가 있다.

<출처: http://sinuk.egloos.com/2676307>

반응형

시스템의 밀리초 구하기.(국제표준시각(UTC, GMT) 1970/1/1/0/0/0 으로부터 경과한 시각)


// 밀리초 단위(*1000은 1초), 음수이면 이전 시각
long time = System.currentTimeMillis ( );
System.out.println ( time.toString ( ) );



현재 시각을 가져오기.


Date today = new Date ();
System.out.println ( today );


결과 : Sat Jul 12 16:03:00 GMT+01:00 2000


경과시간(초) 구하기


long time1 = System.currentTimeMillis ();
long time2 = System.currentTimeMillis ();

system
.out.println ( ( time2 - time1 ) / 1000.0 );


Date를 Calendar로 맵핑하기


Date d = new Date ( );
Calendar c = Calendar.getInstance ( );
c
.setTime ( d );


날짜(년/월/일/시/분/초) 구하기


import java.util.*;
import java.text.*;

SimpleDateFormat formatter = new SimpleDateFormat ( "yyyy.MM.dd HH:mm:ss", Locale.KOREA );
Date currentTime = new Date ( );
String dTime = formatter.format ( currentTime );
System.out.println ( dTime );



날짜(년/월/일/시/분/초) 구하기2


GregorianCalendar today = new GregorianCalendar ( );

int year = today.get ( today.YEAR );
int month = today.get ( today.MONTH ) + 1;
int yoil = today.get ( today.DAY_OF_MONTH );


GregorianCalendar gc = new GregorianCalendar ( );


System.out.println ( gc.get ( Calendar.YEAR ) );
System.out.println ( String.valueOf ( gc.get ( Calendar.MONTH ) + 1 ) );
System.out.println ( gc.get ( Calendar.DATE ) );
System.out.println ( gc.get ( DAY_OF_MONTH ) );



날짜(년/월/일/시/분/초) 구하기3


DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.KOREA);
Calendar cal = Calendar.getInstance(Locale.KOREA);
nal
= df.format(cal.getTime());


- 표준시간대를 지정하고 날짜를 가져오기.


TimeZone jst = TimeZone.getTimeZone ("JST");
Calendar cal = Calendar.getInstance ( jst ); // 주어진 시간대에 맞게 현재 시각으로 초기화된 GregorianCalender 객체를 반환.// 또는 Calendar now = Calendar.getInstance(Locale.KOREA);
System.out.println ( cal.get ( Calendar.YEAR ) + "년 " + ( cal.get ( Calendar.MONTH ) + 1 ) + "월 " + cal.get ( Calendar.DATE ) + "일 " + cal.get ( Calendar.HOUR_OF_DAY ) + "시 " +cal.get ( Calendar.MINUTE ) + "분 " + cal.get ( Calendar.SECOND ) + "초 " );

결과 : 2000년 8월 5일 16시 16분 47초


영어로된 날짜를 숫자로 바꾸기


Date myDate = new Date ( "Sun,5 Dec 1999 00:07:21" );
System.out.println ( myDate.getYear ( ) + "-" + myDate.getMonth ( ) + "-" + myDate.getDay ( ) );



"Sun, 5 Dec 1999 00:07:21"를 "1999-12-05"로 바꾸기


SimpleDateFormat formatter_one = new SimpleDateFormat ( "EEE, dd MMM yyyy hh:mm:ss",Locale.ENGLISH );
SimpleDateFormat formatter_two = new SimpleDateFormat ( "yyyy-MM-dd" );

String inString = "Sun, 5 Dec 1999 00:07:21";


ParsePosition pos = new ParsePosition ( 0 );
Date frmTime = formatter_one.parse ( inString, pos );
String outString = formatter_two.format ( frmTime );


System.out.println ( outString );



숫자 12자리를, 다시 날짜로 변환하기


Date conFromDate = new Date();
long ttl = conFromDate.parse ( "Dec 25, 1997 10:10:10" );
System.out.println ( ttl ); //예 938291839221

Date today = new Date ( ttl );
DateFormat format = DateFormat.getDateInstance ( DateFormat.FULL,Locale.US );
String formatted = format.format ( today );
System.out.println ( formatted );



특정일로부터 n일 만큼 이동한 날짜 구하기

특정일의 시간을 long형으로 읽어온다음..
날짜*24*60*60*1000 을 계산하여.
long형에 더해줍니다.
그리고 나서 Date클래스와 Calender클래스를 이용해서 날짜와 시간을 구하면 됩니다


특정일에서 일정 기간후의 날짜 구하기2


//iDay 에 입력하신 만큼 빼거나 더한 날짜를 반환 합니다.
import java.util.*;

public String getDate ( int iDay )
{

Calendar temp=Calendar.getInstance ( );
StringBuffer sbDate=new StringBuffer ( );


temp.add ( Calendar.DAY_OF_MONTH, iDay );


int nYear = temp.get ( Calendar.YEAR );
int nMonth = temp.get ( Calendar.MONTH ) + 1;
int nDay = temp.get ( Calendar.DAY_OF_MONTH );


sbDate.append ( nYear );
if ( nMonth < 10 )
sbDate
.append ( "0" );

sbDate
.append ( nMonth );
if ( nDay < 10 )
sbDate
.append ( "0" );

sbDate
.append ( nDay );


return sbDate.toString ( );
}



현재날짜에서 2달전의 날짜를 구하기


Calendar cal = Calendar.getInstance ( );//오늘 날짜를 기준으루..
cal
.add ( cal.MONTH, -2 ); //2개월 전....
System.out.println ( cal.get ( cal.YEAR ) );
System.out.println ( cal.get ( cal.MONTH ) + 1 );
System.out.println ( cal.get ( cal.DATE ) );


달에 마지막 날짜 구하기


for ( int month = 1; month <= 12; month++ )
{

GregorianCalendar cld = new GregorianCalendar ( 2001, month - 1, 1 );
System.out.println ( month + "/" + cld.getActualMaximum ( Calendar.DAY_OF_MONTH ) );
}


해당하는 달의 마지막 일 구하기


GregorianCalendar today = new GregorianCalendar ( );
int maxday = today.getActualMaximum ( ( today.DAY_OF_MONTH ) );
System.out.println ( maxday );


특정일을 입력받아 해당 월의 마지막 날짜를 구하는 간단한 예제.(달은 -1 해준다.)...윤달 30일 31일 알아오기.


Calendar cal = Calendar.getInstance ( );
cal
.set ( Integer.parseInt ( args[0] ), Integer.parseInt ( args [1] ) - 1, Integer.parseInt ( args [2] ) );
SimpleDateFormat dFormat = new SimpleDateFormat ( "yyyy-MM-dd" );
System.out.println ( "입력 날짜 " + dFormat.format ( cal.getTime ( ) ) );
System.out.println ( "해당 월의 마지막 일자 : " + cal.getActualMaximum ( Calendar.DATE ) );


해당월의 실제 날짜수 구하기 ( 1999년 1월달의 실제 날짜수를 구하기 )


Calendar calendar = Calendar.getInstance ( );
calendar
.set ( 1999, 0, 1 );
int maxDays = calendar.getActualMaximum ( Calendar.DAY_OF_MONTH );


어제 날짜 구하기

오늘날짜를 초단위로 구해서 하루분을 빼주고 다시
셋팅해주면 쉽게 구할수 있죠..
setTime((기준일부터 오늘까지의 초를 구함) - 24*60*60)해주면 되겠죠..

어제 날짜 구하기2


import java.util.*;

public static Date getYesterday ( Date today )
{
if ( today == null )
throw new IllegalStateException ( "today is null" );

Date yesterday = new Date ( );
yesterday
.setTime ( today.getTime ( ) - ( (long) 1000 * 60 * 60 * 24 ) );


return yesterday;
}



내일 날짜 구하기


Date today = new Date ( );
Date tomorrow = new Date ( today.getTime ( ) + (long) ( 1000 * 60 * 60 * 24 ) );


내일 날짜 구하기2


Calendar today = Calendar.getInstance ( );
today
.add ( Calendar.DATE, 1 );
Date tomorrow = today.getTime ( );


오늘날짜에서 5일 이후 날짜를 구하기


Calendar cCal = Calendar.getInstance();
c
.add(Calendar.DATE, 5);


날짜에 해당하는 요일 구하기


//DAY_OF_WEEK리턴값이 일요일(1), 월요일(2), 화요일(3) ~~ 토요일(7)을 반환합니다.
//아래 소스는 JSP일부입니다.
import java.util.*;

Calendar cal= Calendar.getInstance ( );
int day_of_week = cal.get ( Calendar.DAY_OF_WEEK );
if ( day_of_week == 1 )
m_week
="일요일";
else if ( day_of_week == 2 )
m_week
="월요일";
else if ( day_of_week == 3 )
m_week
="화요일";
else if ( day_of_week == 4 )
m_week
="수요일";
else if ( day_of_week == 5 )
m_week
="목요일";
else if ( day_of_week == 6 )
m_week
="금요일";
else if ( day_of_week == 7 )
m_week
="토요일";



콤보박스로 선택된 날짜(예:20001023)를 통해 요일을 영문으로 가져오기


//gc.get(gc.DAY_OF_WEEK); 하면 일요일=1, 월요일=2, ..., 토요일=7이 나오니까,
//요일을 배열로 만들어서 뽑아내면 되겠죠.
GregorianCalendar gc=new GregorianCalendar ( 2000, 10 - 1 , 23 );
String [] dayOfWeek = { "", "Sun", "Mon", .... , "Sat" };

String yo_il = dayOfWeek ( gc.get ( gc.DAY_OF_WEEK ) );



두 날짜의 차이를 일수로 구하기

각각의 날짜를 Date형으로 만들어서 getTime()하면
long으로 값이 나오거든요(1970년 1월 1일 이후-맞던가?- 1/1000 초 단위로..)
그러면 이값의 차를 구해서요. (1000*60*60*24)로 나누어 보면 되겠죠.


두 날짜의 차이를 일수로 구하기2


import java.io.*;
import java.util.*;

Date today = new Date ( );
Calendar cal = Calendar.getInstance ( );
cal
.setTime ( today );// 오늘로 설정.


Calendar cal2 = Calendar.getInstance ( );
cal2
.set ( 2000, 3, 12 ); // 기준일로 설정. month의 경우 해당월수-1을 해줍니다.


int count = 0;
while ( !cal2.after ( cal ) )
{
count
++;
cal2
.add ( Calendar.DATE, 1 ); // 다음날로 바뀜


System.out.println ( cal2.get ( Calendar.YEAR ) + "년 " + ( cal2.get ( Calendar.MONTH ) + 1 ) + "월 " + cal2.get ( Calendar.DATE ) + "일" );
}


System.out.println ( "기준일로부터 " + count + "일이 지났습니다." );



두 날짜의 차이를 일수로 구하기3


import java.io.*;
import java.util.*;

public class DateDiff
{
public static int GetDifferenceOfDate ( int nYear1, int nMonth1, int nDate1, int nYear2, int nMonth2, int nDate2 )
{
Calendar cal = Calendar.getInstance ( );
int nTotalDate1 = 0, nTotalDate2 = 0, nDiffOfYear = 0, nDiffOfDay = 0;


if ( nYear1 > nYear2 )
{
for ( int i = nYear2; i < nYear1; i++ )
{

cal
.set ( i, 12, 0 );
nDiffOfYear
+= cal.get ( Calendar.DAY_OF_YEAR );
}
nTotalDate1
+= nDiffOfYear;
}
else if ( nYear1 < nYear2 )
{
for ( int i = nYear1; i < nYear2; i++ )
{
cal
.set ( i, 12, 0 );
nDiffOfYear
+= cal.get ( Calendar.DAY_OF_YEAR );
}
nTotalDate2
+= nDiffOfYear;
}


cal.set ( nYear1, nMonth1-1, nDate1 );
nDiffOfDay
= cal.get ( Calendar.DAY_OF_YEAR );
nTotalDate1
+= nDiffOfDay;


cal.set ( nYear2, nMonth2-1, nDate2 );
nDiffOfDay
= cal.get ( Calendar.DAY_OF_YEAR );
nTotalDate2
+= nDiffOfDay;


return nTotalDate1-nTotalDate2;
}


public static void main ( String args[] )
{
System.out.println ( "" + GetDifferenceOfDate (2000, 6, 15, 1999, 8, 23 ) );
}
}



파일에서 날짜정보를 가져오기


File f = new File ( directory, file );

Date date = new Date ( f.lastModified ( ) );
Calendar cal = Calendar.getInstance ( );
cal
.setTime ( date );


System.out.println("Year : " + cal.get(Calendar.YEAR));
System.out.println("Month : " + (cal.get(Calendar.MONTH) + 1));
System.out.println("Day : " + cal.get(Calendar.DAY_OF_MONTH));
System.out.println("Hours : " + cal.get(Calendar.HOUR_OF_DAY));
System.out.println("Minutes : " + cal.get(Calendar.MINUTE));
System.out.println("Second : " + cal.get(Calendar.SECOND));



날짜형식으로 2000-01-03으로 처음에 인식을 시킨후
7일씩 증가해서 1년정도의 날짜를 출력해 주고 싶은데요.


SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy-mm-dd" );
Calendar c = Calendar.getInstance ( );

for ( int i = 0; i < 48; i++ )
{
c
.clear ( );
c
.set ( 2000, 1, 3 - ( i * 7 ) );
java
.util.Date d = c.getTime ( );
String thedate = sdf.format ( d );
System.out.println ( thedate );
}



쓰레드에서 날짜 바꾸면 죽는 문제

Main화면에 날짜와시간이Display되는 JPanel이 있습니다.
date로 날짜와 시간을 변경하면 Main화면의 날짜와 시간이 Display되는 Panel에
변경된 날짜가 Display되지 않고 Main화면이 종료되어 버립니다.

문제소스:


public void run ( )
{
while ( true )
{
try{
timer
.sleep ( 60000 );
}
catch ( InterruptedException ex ) { }

lblTimeDate.setText ( fGetDateTime ( ) );
repaint
( );

}
}


public String fGetDateTime ( )
{
final int millisPerHour = 60 * 60 * 1000;
String DATE_FORMAT = "yyyy / MM / dd HH:mm";
SimpleDateFormat sdf = new SimpleDateFormat ( DATE_FORMAT );
SimpleTimeZone timeZone = new SimpleTimeZone ( 9 * millisPerHour, "KST" );
sdf
.setTimeZone ( timeZone );


long time = System.currentTimeMillis ( );
Date date = new Date ( time );
return sdf.format ( date );
}

해답:


// 날짜와 요일 구한다. timezone 으로 날짜를 다시 셋팅하시면 됨니다.
public String getDate ( )
{
Date now = new Date ( );
SimpleDateFormat sdf4 = new SimpleDateFormat ( "yyyy/MM/dd HH:mm EE" );
sdf4
.setTimeZone ( TimeZone.getTimeZone ( "Asia/Seoul" ) );

return sdf4.format ( now );
}



날짜와 시간이 유효한지 검사하려면...?


import java.util.*;
import java.text.*;

public class DateCheck
{

boolean dateValidity = true;


DateCheck ( String dt )
{
try

{

DateFormat df = DateFormat.getDateInstance ( DateFormat.SHORT );
df
.setLenient ( false );
Date dt2 = df.parse ( dt );

}

catch ( ParseException e ) { this.dateValidity = false; }
catch ( IllegalArgumentException e ) { this.dateValidity = false; }
}


public boolean datevalid ( )
{
return dateValidity;

}


public static void main ( String args [] )
{

DateCheck dc = new DateCheck ( "2001-02-28" );
System.out.println ( " 유효한 날짜 : " + dc.datevalid ( ) );
}
}



두 날짜 비교하기(아래보다 정확)

그냥 날짜 두개를 long(밀리 세컨드)형으로 비교하시면 됩니다...

이전의 데이타가 date형으로 되어 있다면, 이걸 long형으로 변환하고.
현재 날짜(시간)은 System.currentTimeMillis()메소드로 읽어들이고,
두수(long형)를 연산하여 그 결과 값으로 비교를 하시면 됩니다.

만약 그 결과값이 몇시간 혹은 며칠차이가 있는지를 계산할려면,
결과값을 Calender의 setTimeInMillis(long millis) 메소드를 이용해
설정한다음 각각의 날짜나 시간을 읽어오시면 됩니다


두 날짜 비교하기2


//Calendar를 쓸 경우 데이타의 원본을 고치기 때문에 clone()을 사용하여
//복사한 후에 그 복사본을 가지고 비교한다
import java.util.*;
import java.util.Calendar.*;
import java.text.SimpleDateFormat;

public class DayComparisonTest
{
public static void main(String args[])
{
Calendar cal = Calendar.getInstance();
SimpleDateFormat dateForm = new SimpleDateFormat("yyyy-MM-dd");


Calendar aDate = Calendar.getInstance(); // 비교하고자 하는 임의의 날짜
aDate
.set(2001, 0, 1);


Calendar bDate = Calendar.getInstance(); // 이것이 시스템의 날짜


// 여기에 시,분,초를 0으로 세팅해야 before, after를 제대로 비교함
aDate
.set( Calendar.HOUR_OF_DAY, 0 );
aDate
.set( Calendar.MINUTE, 0 );
aDate
.set( Calendar.SECOND, 0 );
aDate
.set( Calendar.MILLISECOND, 0 );


bDate.set( Calendar.HOUR_OF_DAY, 0 );
bDate
.set( Calendar.MINUTE, 0 );
bDate
.set( Calendar.SECOND, 0 );
bDate
.set( Calendar.MILLISECOND, 0 );



if (aDate.after(bDate)) // aDate가 bDate보다 클 경우 출력
System.out.println("시스템 날짜보다 뒤일 경우 aDate = " + dateForm.format(aDate.getTime()));
else if (aDate.before(bDate)) // aDate가 bDate보다 작을 경우 출력

System.out.println("시스템 날짜보다 앞일 경우 aDate = " + dateForm.format(aDate.getTime()));
else // aDate = bDate인 경우
System.out.println("같은 날이구만");
}
}

<출처 : 서비의 다락방 ( http://www.yunsobi.com )>
-출처를 밝혀주신 당신은 아름다운 사람입니다.-
반응형

1. J2SE 1.5 버전이면 java.util.Scanner 클래스   API를 참고

ex) Scanner
       System.out.print("입력값:");
      
Scanner  scan  =  new  Scanner (System.in);
       String  testStr = scan.next();

좀더 자세한 사항은 API 뒤져보세요.. API 문서 활용을 생활화 합시다.ㅋ

2. 대부분 많이 사용하는  java.io.BufferedReader

ex) BufferedReader
      
System.out.print("입력하세요: ");
       BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
       String testStr = br.readLine();

3. 이 외에도 한글자만 받아들이는 방법으론
     char c = (char)System.in.read();  //0~255 사이의 아스키코드 한글자


상황에 따라서 try ~ catch   예외처리 해주시는것도 잊지마삼 ^^

반응형
자바 웹 스타트(JWS)는 웹 기반으로 애플리케이션을 배포할 수 있지 않습니까? 그런데 사람들은 왜 CD-ROM으로 자바 웹 스타트(JWS) 애플리케이션을 배포하려고 할까요? 이유는 여러 가지입니다. 우선, 대규모 애플리케이션이라면 고속 광대역 회선으로도 설치 프로그램 전체를 다운로드하기가 만만치 않을 수 있습니다. 둘째로, 기업 보안 등의 이유로 인해 온라인 연결이 안 되는 데스크탑도 많고 또 인터넷에 액세스할 수 없는 시스템도 많습니다. 마지막으로, 그저 CD가 더 좋다는 사람들이 많기 때문입니다.

클라이언트 기업에서 광대역 회선이 거의 없는 지역을 비롯하여 전 세계에 자사의 애플리케이션을 배포해 달라고 요청합니다. 이 애플리케이션에는 수많은 제품에 대한 정보와 함께 상세한 도면과 다이어그램까지 들어 있습니다. 애플리케이션의 상당 부분은 이러한 정보로 구성되어 있으며 JVM을 포함한 전체 설치 용량은 40MB를 넘어섭니다. 또 이 회사에서는 무역 박람회 등에서도 홍보물과 함께 이 애플리케이션을 CD에 담아 배포하고 싶다고 합니다. 그러므로 CD 배포도 준비해야 합니다. 보통, CD 설치에는 여러 가지 상업적 설치 프로그램이나 오픈소스 설치 프로그램을 이용하게 됩니다. 그러나 자바 웹 스타트로 실행하게 되어 있는 애플리케이션은 보통의 설치 프로그램에서와 달리 특정 위치에 설치해야 하며 사용자가 재량을 발휘할 여지가 없습니다.

이 기사에서는 CD와 인터넷 양쪽 모두를 이용하는 애플리케이션 설치 단계에 대해 설명합니다. 설치 프로세스에 필요한 조건은 다음과 같습니다.

  1. 설치된 애플리케이션은 업데이트를 스스로 확인하고 JWS 캐시와 통합되어야 합니다.
  2. 기존 또는 최신 버전의 자바 없이도 설치 후 즉시 시스템에서 실행할 수 있어야 합니다.
  3. 설치된 애플리케이션에는 인터넷 연결이 필요 없습니다.
  4. 설치된 프로그램은 사용하기 쉬워야 하며 사용자 인터페이스는 간단해야 합니다.

애플리케이션 설치는 보통 일반적인 설치 프로그램을 사용하여 이루어집니다. 그러나 기존의 설치 프로세스에서는 효율성 제고를 위해 JWS를 인식하지 못하는 별도의 애플리케이션을 작성하는 경우가 많았습니다. 따라서 업데이트가 발표될 때마다 사용자는 새 버전을 다운로드하여 설치해야 했습니다. 이와 달리 JWS 애플리케이션은 업데이트된 구성요소만 다운로드하므로 프로세스의 효율성과 신뢰성이 훨씬 더 높습니다. 그러므로 이 기사에서는 JWS 애플리케이션 설치 프로그램에 대해서도 설명하고자 합니다.


JWS 프라이머

자바 웹 스타트가 있으면 JNLP 파일 링크를 통해 자바 애플리케이션을 시작할 수 있습니다. 이 JNLP 파일은 애플리케이션에 대한 진입점 또는 기본 메소드를 설명하는 한편 애플리케이션에서 사용할 리소스를 참조합니다.

JWS 애플리케이션을 시작하면 JVM이 필요한 리소스에 액세스하려고 시도하면서, 경우에 따라 리소스를 업데이트하고 파일을 캐시로 복사합니다. 그 뒤로 이 애플리케이션을 시작하면 JWS가 이 캐시부터 확인하므로 리소스 다운로드 단계를 건너뛸 수 있습니다. 클라이언트 시스템이 오프라인 상태이거나 서버에 연결할 수 없는 경우, JWS는 오프라인 모드로 애플리케이션을 실행합니다.

JWS 시작 파일(JNLP 파일)이 CD에 들어 있는 경우, JWS는 서버에 연결하여 새 파일을 다운로드하려고 시도합니다. 물론 클라이언트 시스템이 온라인 상태일 때 이렇게 하는 것은 CD를 이용한 파일 배포의 취지에 어긋나는 일입니다. 대신에 우리는 앞서 JWS가 애플리케이션을 로드한 것처럼 JWS 캐시를 업데이트할 방법을 찾아야 합니다.


JWS 캐시 업데이트

자바 5 버전의 JWS에는 잘 알려진 -import 옵션이 있습니다. 이것은 특정 위치의 JWS 애플리케이션을 캐시로 가져오는 옵션입니다.

이 위치에 있는 CD 이미지는 보통 웹 서버에 배치되는 것, 즉 JNLP 파일과 이 JNLP 파일에서 참조하는 리소스 및 .jar 파일 등의 사본에 불과합니다. 서블릿을 사용하여 JNLP를 서비스하는 경우, CD 이미지를 실행하려면 생성된 JNLP 파일의 완벽한 스냅샷이 필요합니다.

따라서 다음을 호출하여 JWS 캐시에 CD 이미지를 설치할 수 있습니다.

<JAVA_HOME>/jre/bin/javaws -codebase <CACHE_IMAGE> -import <CACHE_IMAGE>/<XXXX>.jnlp

여기서 <JAVA_HOME>은 새 JVM 또는 기존 JVM의 루트이고, <CACHE_IMAGE>은 CD에서 JWS 애플리케이션의 위치이며, <XXXX>는 애플리케이션 JNLP 파일의 이름입니다. 이 명령을 자동화하고 간단한 GUI로 래핑하는 방법은 나중에 설명하겠습니다.

캐시에 저장된 애플리케이션을 설치하는 동안 JWS는 애플리케이션 시작을 위한 단축키를 바탕 화면이나 메뉴에 설치할지 묻는 메시지를 표시합니다. JWS 설치가 완료된 후 JWS를 다시 호출하여 새로 설치한 애플리케이션을 시작할 수 있습니다.

<JAVA_HOME>/jre/bin/javaws -import <CACHE_IMAGE>/<XXXX>.jnlp

여기서 다시 한 번 CD를 사용합니다. 그러나 이번에는 JWS가 JNLP 파일에서 참조하는 설치 리소스를 사용하게 됩니다. 시스템이 인터넷에 연결되어 있을 때는 통상적인 방법으로 업데이트를 확인한 다음, 애플리케이션을 시작합니다. 네트워크 연결이 설정되지 않았다면 CD에 저장된 상태 그대로 애플리케이션이 시작됩니다.

다음 번에 이 애플리케이션을 시작하는 사용자는 메뉴 또는 바탕 화면의 단축키를 사용할 수 있으며 CD는 이제 필요 없습니다. 아니면 웹 페이지의 링크를 사용하여 애플리케이션을 시작할 수 있습니다. 이 링크는 동일한 URL/JNLP 파일 조합, 예를 들면 해당 웹 사이트에 있던 원래 버전을 가리키는 링크여야 합니다.


JVM 문제

지금까지 설명한 내용에는 허점이 하나 있습니다. 위의 명령을 실행하려면 JVM이 있어야 하는데, 드물기는 하지만 JVM이 설치되어 있지 않거나 시스템 경로에 기본값으로 설정되어 있지 않아서 별도의 조치를 통해 사용 가능한 JVM을 찾아야 하는 경우가 있습니다. 이와 함께 사용자가 CD를 넣으면 설치가 시작되고 그 과정에서 기존 JVM이 있는지 자동으로 확인해야 합니다. 따라서 JVM 확인 프로세스는 다음과 같이 이루어집니다.

  1. 설치 프로그램이 JVM을 확인합니다.
  2. 없으면 JVM을 설치합니다.
  3. 설치 프로그램이 시작되고 사용자 라이센스 정보가 표시됩니다.
  4. 대상 JVM(위 1과 다른 버전을 애플리케이션이 요구하는 경우, 해당 버전)을 설치합니다.
  5. JWS 캐시를 가져옵니다.
  6. JWS 애플리케이션을 시작합니다.

JWS -import 옵션을 사용하기 위한 최소 JVM 버전이 자바 5라는 사실 때문에 몇 가지 문제가 더 발생합니다. 다시 말해, JVM이 설치되어 있고 애플리케이션에 사용할 수 있는 상태이더라도 이 import 옵션을 지정하려면 그 이상의 최신 JVM이 필요하게 됩니다. 둘째, 가져오기 프로세스에는 시간이 약간 걸리는데 애플리케이션을 시작하려면 먼저 이 프로세스를 마쳐야 합니다. 이렇게 실행이 지연되면 일반적인 다른 설치 프로그램과 같은 성능을 얻기가 어려워집니다.

이러한 문제를 고려한 결과, 위의 단계를 자동으로 수행할 수 있는 맞춤 시작형 애플리케이션을 제작하기로 했습니다.


설치 프로그램 실행

설치 프로세스의 실제 과정은 대부분 JWS -import 명령으로 처리되므로, 이 시작 애플리케이션의 주된 임무는 적절한 명령으로 JVM을 찾아 시작한 뒤 사용자에게 작업의 진행 과정을 GUI로 알려주는 것입니다.


JVM 찾기

Windows에서는 시스템 레지스트리의 HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Runtime Environment 키 아래에서 JVM을 찾을 수 있습니다. 이 키는 값이 여러 개일 수 있으며, 따라서 시작 애플리케이션은 레지스트리 항목을 반복하여 점검하면서 사용 가능한 최신 버전의 JVM을 찾아냅니다.

다음 메소드는 최소 및 최대 버전 번호의 인수를 대입하여 JVM 경로를 찾으려고 시도합니다.


private String getInstalledPath(
 
int majorMin, int minorMin, int revMin,
 
int majorMax, int minorMax, int revMax )
 
{
   
String installedPath = null;
   
int latestVersion = 0;

   
String keyRoot =
         
"HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft" +
         
"\\Java Runtime Environment";
   
Vector results = getRegEntries( "\"" + keyRoot + "\" /s" );
   
int numEntries = results.size();
   
for ( int i = 0; i < numEntries; i++ ) {
   
String key = results.get( i++ ).toString();
   
int pos = key.indexOf( "Java Runtime Environment" );
   
if ( pos > 0 ) {
      pos
+= "Java Runtime Environment".length() + 1;
     
String version = key.substring( pos );
     
String parts[] = version.split( "[._]" );
     
int majorVersion, minorVersion, revision;
      majorVersion
= Integer.parseInt( parts[ 1 ] );
     
if ( parts.length > 3 )
        minorVersion
= Integer.parseInt( parts[ 2 ] );
     
else
        minorVersion
= 0;
     
     
if ( parts.length > 4 )
        revision
= Integer.parseInt( parts[ 3 ] );
     
else
        revision
= 0;
       
     
if ((( majorVersion == -1 ) ||
           
( majorVersion >= majorMin )) &&

         
(( majorVersion == -1 ) ||
           
( majorVersion <= majorMax ))) {
       
if ((( minorMin == -1 ) ||
             
( minorVersion >= minorMin )) &&
           
(( minorMax == -1 ) ||
             
( minorVersion <= minorMax ))) {
         
if ((( revMin == -1 ) ||
               
( revision >= revMin )) &&
             
(( revMax == -1 ) ||
               
( revision <= revMax ))) {
           
// Prefer the neweset acceptable version
           
int thisVersion = majorVersion * 10000 +
                minorVersion
* 100 + revision;
           
if ( thisVersion > latestVersion ) {
             
String value = null;
             
while ( i < numEntries ) {
                value
= results.get( i++ ).toString().trim();
               
if ( value.startsWith( "JavaHome" ))
                 
break;
             
}
             
              installedPath
= value.substring(
                value
.indexOf( "REG_SZ" ) +  6 ).trim();
              latestVersion
= thisVersion;
           
}
         
}
       
}
     
}
   
}
 
}

 
return installedPath;
}

위 메소드의 키는 레지스트리 항목을 찾기 위한 키입니다. 레지스트리 값을 찾아주는 API는 여러 가지 있지만, 이 상황에서 가장 실용적인 것은 간단한 명령행 REG QUERY <key>입니다. 여기서 <key>는 쿼리할 레지스트리 경로입니다. 다음 메소드는 명령을 실행한 다음, 출력된 스트림 항목을 읽어 이를 Vector로 반환합니다.


private Vector getRegEntries( String key )
{
 
Vector results = new Vector();

 
try {
   
Process proc = Runtime.getRuntime().exec( "REG QUERY " +
      key
);
   
InputStream is = proc.getInputStream();
   
InputStreamReader isr = new InputStreamReader(is);


   
BufferedReader br = new BufferedReader(isr);
   
String result = "";
   
String line;

   
while (( line = br.readLine()) != null ) {
      line
= line.trim();
      results
.add( line );
   
}

   
return results;
 
}
 
catch ( Exception ex ) {
    message
( language.getString("6") + ex.getMessage() );
    ex
.printStackTrace();
 
}

 
return null;
}

JVM 설치

적당한 JVM을 찾을 수 없으면 CD에서 설치하면 됩니다. 단, 그 위치를 알고 있어야 합니다. 시작 프로그램은 properties 파일을 사용하여 필요한 여러 가지 리소스를 파악한 다음, 이 메커니즘을 통해 JVM 설치 패키지로 보낼 수 있습니다. 여기서도 마찬가지로, JVM 설치를 시작하려면 올바른 명령을 실행할 수 있어야 합니다. 그러나 이번에는 프로세스의 실행 시간이 비교적 길기 때문에 완료될 때까지 조금 기다려야 합니다. 실행(exec)된 프로세스의 waitFor 메소드는 완료될 때까지 스레드를 대기시키는 한편 UI 스레드의 차단을 방지하기 위해 이를 SwingWorker 안에 중첩시킵니다.


private void installJre()
{
 
try {
   
final String javaInstall = (String)props.get(
       
"jre_installer" );
    status
.setText( language.getString("3") );

   
SwingWorker worker = new SwingWorker()
   
{
     
public Object construct()
     
{
       
try {
         
Process process = Runtime.getRuntime().exec(
            workingDir
+ File.separatorChar +
            javaInstall
);
          process
.waitFor();
          exitValue
= new Integer( process.exitValue());
       
}
       
catch ( Exception ex ) {
          exitValue
= new Integer( -1 );
          ex
.printStackTrace();
       
}

       
return exitValue;
     
}

     
public void finished()
     
{
       
int ev = exitValue.intValue();
       
if ( exitValue != 0 ) {
          status
.setText( language.getString("Error:_") +
            exitValue
);
          message
( language.getString("4") );
       
}
       
else {
          installedJrePath
= getInstalledPath( 5, -1, -1,
                                               
5, -1, -1 );
          doInstall
( installedJrePath );
       
}
        status
.setText( "" );
     
}
   
};
    worker
.start();
 
}
 
catch ( Exception ex ) {
    status
.setText( language.getString( "5" ));
    ex
.printStackTrace();
 
}
}

JWS 시작 후 기다리기

JVM을 찾았으면 적절한 명령으로 이를 시작할 수 있습니다. JWS를 호출할 때, 첫 번째 가져오기 호출은 복사 및 완료하는 데 상당한 시간이 걸릴 수 있으므로 위와 같이 waitFor 메소드를 다시 사용합니다. 그러나 두 번째 호출부터는 애플리케이션을 시작하기만 하면 자동으로 처리됩니다. 일단 애플리케이션이 시작되면 시작 애플리케이션은 임무를 다한 것이므로 종료됩니다.


private void launchWebStart( String javaWSPath,
                             
String jnlpPath,
                             
String userDir )
{
 
try {
   
String webStartCommand = "\"" + javaWSPath + "\"" + " -wait
      -codebase file:///"
+ userDir + "\\"+ appDirectory +
     
" -import " + jnlpPath;
   
Process process = Runtime.getRuntime().exec(
      webStartCommand
);
    process
.waitFor();
   
int exitValue = process.exitValue();
   
if ( exitValue != 0 )
      status
.setText( language.getString("7") );
   
else
      status
.setText( language.getString("8") );


   
int rc = JOptionPane.showConfirmDialog( null,
      language
.getString("9"),
      language
.getString("10"),
     
JOptionPane.YES_NO_OPTION );
   
if ( rc == JOptionPane.YES_OPTION )
     
Runtime.getRuntime().exec( javaWSPath + " -offline " +
        jnlpPath
);

    status
.setText( language.getString("11") );
   
SwingWorker worker = new SwingWorker()
   
{
     
public Object construct()
     
{
       
try {
         
Thread.currentThread().sleep( 3000L );
       
}
       
catch ( Exception ex ) {
       
}

       
return null;
     
}

     
public void finished()
     
{
       
System.exit( 0 );
     
}
   
};
    worker
.start();
 
}
 
catch ( Exception ex ) {
    status
.setText( language.getString("12") );
    ex
.printStackTrace();
 
}
}


패키지 작성

설치를 위한 메커니즘은 모두 처리했습니다. 이제 시작 프로그램은 간단한 UI(아래 그림 1 참조)를 사용하여 사용자에게 진행 상황을 알려야 합니다.

사용자 삽입 이미지
그림 1. 설치 프로그램 시작 페이지(큰 그림으로 보려면 이미지 클릭)

UI는 현지화되어 있으며 로고, 애플리케이션 위치, JVM 버전 등을 config.properties 파일에서 구성할 수 있습니다. 소스를 포함한 전체 애플리케이션은 Aria project에서 오픈소스 라이센스로 구할 수 있습니다.

시작 애플리케이션을 제작하고 테스트까지 마친 뒤에는 완벽한 CD 설치 이미지를 작성하는 마지막 단계 하나만 남습니다. 그것이 바로 CD를 넣었을 때 설치를 시작하는 autorun 기능입니다. Windows용 autorun.inf 파일을 작성하는 방법에 대한 자세한 내용은 위키피디아의 Autorun 항목을 참조하십시오. 그러나 이 기능에는 원시 실행 파일이 필요합니다. 이러한 실행 파일을 작성하려면 Launch4J 래퍼를 사용하면 됩니다. Launch4J의 샘플 구성 파일은 다운로드한 소스에 포함되어 있습니다. Launch4J를 실행하면 해당 애플리케이션의 .exe 파일이 작성됩니다. 위에서 설명한 시작 애플리케이션은 자바 애플리케이션이고 사용자 시스템에는 JVM이 없을 수도 있으므로 여기서도 JVM을 래퍼와 함께 번들로 묶어야 합니다.

이렇게 .exe 파일을 작성한 다음, autorun.inf 파일을 작성하여 CD 이미지에 추가하면 됩니다.


[autorun]
open
=XXXX.exe
icon
=xxxx.ico
action
=Open XXXX
label
=My Application


플랫폼 간 문제

위에서 설명한 애플리케이션 시작 프로그램은 Windows에만 있는 몇 가지 기능을 사용하며, 따라서 Windows에서만 실행됩니다. 그러나 이 기사에서 설명한 방법은 여러 플랫폼에서 응용할 수 있는 방법이며, 다른 플랫폼을 사용하는 경우에는 기본 시작 프로그램을 사용할 수 있는지 여부만 확인하면 됩니다. 예를 들어, IzPack에는 몇 가지 플랫폼용의 시작 프로그램이 포함되어 있습니다. 또한 Mac OS X 처럼 문제의 플랫폼에 JVM이 있는 것이 확실하면 JVM에서 직접 시작 애플리케이션을 실행할 수 있으므로 기본 시작 프로그램은 필요가 없습니다.


결론

자바 웹 스타트와 Launch4J를 조합하여 CD를 작성하면 무역 박람회의 관람객 등에게 이 CD를 배포하여 빠르고 쉽게 애플리케이션을 설치하도록 할 수 있습니다. 사용자는 자바 웹 스타트의 전체 업데이트 기능을 사용하여 손쉽게 업데이트를 구할 수도 있고, 인터넷 연결이 불가능한 경우에는 애플리케이션 자체를 이용할 수도 있으므로 양쪽의 장점만 취하는 방법입니다.


참고 자료

Luan O'Carroll은 소프트웨어 개발자로서 Aria 프로젝트에 선임 연구원으로 참여하고 있습니다.


이 글의 영문 원본은
Distributing a Java Web Start Application via CD-ROM
에서 보실 수 있습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License

<출처: http://www.sdnkorea.com/blog/638 >
반응형

1> 변수의 초기화란?
    선언된 변수에 최초로 값을 할당하는 것.
   즉, 멤버변수로 선언시 프로그래머가 초기값을 주지 않는경우 자동적으로 값이 할당되는 것.
지역변수는 해당이 안됨.

2> 변수의 종류-- 멤버변수와 지역변수

자바에서 변수로는 멤버변수(=전역변수)와 로컬변수(지역변수) 두 종류가 있다.

멤버변수(Member Variable)
로컬변수(local Variable)
선언부분
클래스내
메소드내
사용범위
해당 클래스내 전체
선언된 해당 메소드내
초기화
자동초기화
안됨.
프로그래머가 직점 초기값을 할당.

3> 기본 데이터형의 초기값

데이터형
기본초기값
boolean
false
char
'\u0000'
byte, short, int, long
0
float
0.0f
double
0.0
object
null

4> 초기화 예제

㉠ Variable_init .java


1 : class Variable_init
2 : {
3 :   public static void main(String[] args)
4 : {
5 :     int x;//x는 main메소드 안에 선언되어 있으므로 지역변수...
6 :     //x=0;
7 :     System.out.println(x);
8 :   }
9 : }


<< 실행 결과 >>

에러가 난다. 그 이유는 변수 x는 main메소드 안에 선언되어 있으므로 지역변수이다. 따라서 자동초기화가 안된다. 따라서 6번라인에서 x에 값을 할당해 주면 에러가 제거된다.


Variable_init2 .java


1 : class Variable_init2
2 : {
3 :    static boolean b;
4 :    static int x;
5 :    static float f;
6 :
7 :   public static void main(String[] args)
8 :   {
9 :       System.out.println("변수 b의 초기값은 : "+b);
10 :     System.out.println("변수 x의 초기값은 : "+x);
11 :     System.out.println("변수 f의 초기값은 : "+f);
12 :    }
13 : }


<< 실행 결과 >>

변수 b의 초기값은 : false
변수 x의 초기값은 : 0
변수 f의 초기값은 : 0.0

변수 b,x,f는 메소드내에 선언된 것이 아니므로 멤버변수들이다. 따라서 프로그래머가 초기값을 할당해 주지 않아도 기본값이 할당된다.

여기서 static이란 키워드가 사용되었는데 이는 객체를 생성하지 않아도 변수를 사용할 수 있게 해주는데 지금은 그렇다는 것만 알고 있자.


클래스 변수와 지역변수, 정적변수등 초기화에 대해 명확히 기억하지 못하고 대충 그럴것이다 생각했었는데
다시한번 보고 넘어가자는 의미에서 정리하였다..

출처:
http://www.it-bank.or.kr/prom/java_main.htm

반응형

 자바 java Generic 에 관한 문서..

C++에서 많이 사용하는 template 같은 넘...

jdk 5.0 이상 버전에 annotation 과 함께 추가된 새로운 기능 중 하나.

+ Recent posts