인터냇에서 찾은 미국사람의 싸이트에서 수정을 하고, 올린 글임..

제대로 컴파일되게 했다..

 

시간을 이용하여, 파일을 만들고. 그 파일을 실시간으로 컴파일하는 샘플 예제이다.

 

import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import com.sun.tools.javac.Main;

 

/**

 * author @knight

 */

public class MakeTodayClass {
  Date today = new Date();
  String todayMillis = Long.toString(today.getTime());
  String todayClass = "z_" + todayMillis;
  String todaySource = todayClass + ".java";

  public static void main (String args[]){
    MakeTodayClass mtc = new MakeTodayClass();
    mtc.createIt();
    if (mtc.compileIt()) {
       System.out.println("Running " + mtc.todayClass + ":\n\n");
       mtc.runIt();
       }
    else
       System.out.println(mtc.todaySource + " is bad.");
    }

  public void createIt() {
    try {
      FileWriter aWriter = new FileWriter(todaySource, true);
      aWriter.write("public class "+ todayClass + "{");
      aWriter.write(" public void print() {");
      aWriter.write(" System.out.println(\""+todayMillis+"\");");
      aWriter.write(" }}\n");
      aWriter.flush();
      aWriter.close();
      }
    catch(Exception e){
      e.printStackTrace();
      }
    }

  public boolean compileIt() {
      try {
        String [] source = { new String(todaySource)};
        com.sun.tools.javac.Main main = new com.sun.tools.javac.Main();
        main.compile(source);
      }  catch (Exception e) {
          e.printStackTrace();
          return false;
      }
    return true;
    }

  public void runIt() {
    try {
      Class params[] = {};
      Object paramsObj[] = {};
      Class thisClass = Class.forName(todayClass);
      Object iClass = thisClass.newInstance();
      Method thisMethod = thisClass.getDeclaredMethod("print", params);
      thisMethod.invoke(iClass, paramsObj);
      }
    catch (Exception e) {
      e.printStackTrace();
      }
    }
 }

'java core' 카테고리의 다른 글

jdk 5.0 Annotation Test Code  (0) 2005.05.11
[펌] RMI(Remote Method Invocation)  (0) 2005.05.11
jdk 5.0) In runtime, java compile.  (0) 2005.04.25
jar  (0) 2005.04.25
Runtime in jdk5.0  (0) 2005.03.26
Annotation  (0) 2005.03.18
Posted by 김용환 '김용환'

댓글을 달아 주세요

jar

java core 2005. 4. 25. 09:55
JAR 파일
JAR 파일 포맷의 힘

Level: Introductory

Pagadala J. Suresh, 소프트웨어 엔지니어, IBM Global Services India
Palaniyappan Thiagarajan, 소프트웨어 엔지니어, IBM Global Services India

2003년 10월 9일

대부분의 자바 프로그래머들은 JAR 파일의 기본 작동에 익숙하다. 하지만 JAR 파일 포맷의 막강한 힘을 아는 개발자는 드물다.

JAR 파일
JAR 파일 포맷은 대중적인 ZIP 파일 포맷을 근간으로 하여 많은 파일들을 하나로 모으는데 사용된다. ZIP 파일과는 달리 JAR 파일은 압축과 디스트리뷰션 뿐만 아니라 라이브러리, 컴포넌트, 플러그인 등의 전개와 캡슐화에도 사용되며 컴파일러나 JVM 같은 툴이 직접 사용하기도 한다. 전개 디스크립터 같이 JAR에 포함된 특별한 파일은 특정 JAR가 취급되는 방법을 툴에 지시한다.

JAR 파일은 다음과 같은 데에 사용된다:

  • 클래스 라이브러리의 분배 및 사용
  • 애플리케이션과 확장용 블록 구현
  • 컴포넌트, 애플릿, 플러그인용 전개 유닛
  • 컴포넌트와 관련된 보조 리소스 패키지

JAR 파일 포맷은 많은 혜택과 기능을 제공하며 ZIP 또는 TAR 같은 전통적인 아카이브 포맷이 줄 수 없는 많은 것들을 제공한다. 이를 테면:

  • 보안. JAR 파일의 내용을 디지틀 방식으로 서명할 수 있다.

  • 다운로드 시간 감소. 애플릿이 JAR 파일로 번들되면 이 애플릿의 클래스 파일과 관련 리소스들은 한 번의 HTTP 트랜잭션에서 브라우저를 통해 다운로드 될 수 있다.

  • 압축. JAR 포맷은 파일의 효율적인 저장을 위해 압축시킨다.

  • 투명한 플랫폼 확장. Java Extensions Framework은 자바 핵심 플랫폼에 기능을 추가할 수 있는 수단을 제공하는데 이 때, 확장의 패키지에 JAR 파일을 사용한다. (Java 3D와 JavaMail이 Sun에서 개발된 확장의 예이다.)

  • 패키지 실링(sealing). JAR 파일에 저장된 패키지는 선택적으로 봉합(seal)되어 버전의 영속성과 보안을 강화할 수 있다. 패키지 봉합은 이 패키지에 정의된 모든 클래스들이 같은 JAR 파일에서 찾을 수 있어야 함을 의미한다.

  • 패키지 버저닝(versioning). JAR 파일은 이것이 포함하고 있는 파일 관련 데이터를 보유하고 있다. 벤더와 버전 정보 등이다.

  • 이식성(Portability). JAR 파일을 핸들링하는 메커니즘은 자바 플랫폼의 핵심 API의 표준의 일부이다.

JAR의 압축과 압축풀기
jar 툴(jar 참조)은 파일을 기본적으로 압축한다. 압축이 풀린 JAR 파일은 압축된 JAR 파일 보다 더 빠르게 로딩될 수 있다. 로딩 시간 동안 파일의 압축 풀기 시간이 줄어들기 때문이다. 하지만 네트워크를 통한 다운로드 시간은 압축이 풀린 파일이 더 길다.

META-INF 디렉토리
대부분의 JAR 파일에는 META-INF 디렉토리가 포함되어 있는데 이는 패키지의 저장과 보안 및 버저닝 정보 같은 확장 설정 데이터를 저장하는데 사용된다. META-INF 디렉토리의 파일과 디렉토리는 Java2platform에서 인식 및 인터프리팅되어 애플리케이션, 확장, 클래스 로더를 설정한다:

  • MANIFEST.MF. manifest 파일은 확장 관련, 패키지 관련 데이터를 정의한다.

  • INDEX.LIST. 이 파일은 jar 툴의 새로운 -i 옵션에 의해 생성되어 애플리케이션 또는 확장에 정의된 패키지의 위치 정보를 포함한다. 이것은 JarIndex 구현의 일부이고 클래스 로더에 의해 사용되어 클래스 로딩 프로세스의 속도를 높인다.

  • xxx.SF. JAR 파일의 서명 파일이다. xxx는 서명자를 나타낸다.

  • xxx.DSA. 서명 파일과 관련된 서명 블록 파일은 JAR 파일의 서명에 사용된 공식 서명을 저장한다.

jar 툴
JAR 파일로 기본적인 태스크를 수행하려면 자바 개발 킷의 일부로 제공되는 Java Archive Tool (jar 툴)을 사용한다. jar 툴을 jar 명령어로 호출한다. 표 1은 일반 애플리케이션이다:

표 1. jar 툴의 일반적인 사용

기능 명령어
개별 파일에서 JAR 파일 만들기 jar cf jar-file input-file...
디렉토리에서 JAR 파일 만들기 jar cf jar-file dir-name
압축 풀린 JAR 파일 만들기 jar cf0 jar-file dir-name
JAR 파일 업데이트 jar uf jar-file input-file...
JAR 파일 내용보기 jar tf jar-file
JAR 파일 내용 추출하기 jar xf jar-file
JAR 파일에서 특정 파일 추출하기 jar xf jar-file archived-file...
실행 JAR 파일로 패키지된 애플리케이션 실행하기 java -jar app.jar

실행 JAR 파일
실행 JAR 파일은 특별히 설정된 JAR 파일에 저장된 독립적인 자바 애플리케이션이다. 파일을 추출하거나 클래스 경로를 설정하지 않고 JVM에 의해 직접 실행될 수 있다. 비 실행 JAR에 저장된 애플리케이션을 구동하려면 이를 클래스 경로에 추가하고 애플리케이션의 메인 클래스를 이름별로 호출해야한다. 하지만 실행 JAR 파일을 사용하면 이를 추출하거나 메인 엔트리 포인트를 알 필요 없이 애플리케이션을 실행할 수 있다.

실행 JAR 파일 만들기
실행 JAR 파일을 만들기는 쉽다. 모든 애플리케이션 코드를 하나의 디렉토리에 놓는 것으로 시작한다. 애플리케이션의 메인 클래스가 com.mycompany.myapp.Sample이라고 가정해보자. 애플리케이션 코드를 포함하고 메인 클래스를 구분하는 JAR 파일 생성이 필요하다. 이를 위해 라는 manifest 파일을 어딘가에(애플리케이션 디렉토리는 아니다) 만들고 여기에 다음 행을 추가한다:


Main-Class: com.mycompany.myapp.Sample

그런 다음 JAR 파일을 다음과 같이 만든다:


jar cmf manifest ExecutableJar.jar application-dir

이제 JAR 파일인 ExecutableJar.jar가 java -jar를 사용하여 실행될 수 있다.

실행 JAR 파일 시작하기
애플리케이션을 ExecutableJar.jar라는 실행 JAR 파일로 묶었으므로 다음 명령어를 사용하여 파일에서 직접 애플리케이션을 시작할 수 있다:


java -jar ExecutableJar.jar

패키지 실링(sealing)
JAR 파일안에 패키지를 봉합(sealing)한다는 것은 이 패키지에 정의된 모든 클래스가 같은 JAR 파일에서 찾아져야 한다는 것을 의미한다. 이로서 패키지 작성자는 패키지된 클래스들의 버전 영속성을 강화할 수 있다. 봉합은 보안 조치도 제공하여 코드 탬퍼링을 탐지한다.

패키지를 봉합하려면 패키지용 Name 헤더를 추가한다. 그 뒤에 Sealed 헤더 값을 JAR manifest 파일에 대해 "true"로 한다. 실행 JAR 파일과 마찬가지로 manifest 파일을 적절한 헤더 엘리먼트로 지정하여 JAR를 봉합할 수 있다:


Name: com/samplePackage/
Sealed: true

Name 헤더는 패키지의 관련 경로명을 정한다. 파일이름과 구별되도록 "/"로 끝난다. Name 헤더에 뒤따르는 모든 헤더는 공백 라인 없이 Name 헤더에 지정된 파일이나 패키지에 붙는다. 위 예제에서 Sealed 헤더가 공백 라인 없이 Name 헤더 다음에 발생했기 때문에 Sealed 헤더는 com/samplePackage 패키지에만 붙는것으로 인터프리팅된다.

JAR 파일 외에 다른 소스에서 봉합된 패키지의 클래스를 로딩하려고 하면 JVM이 SecurityException을 던진다.

확장 패키징
확장은 자바 플랫폼에 기능을 추가한다. 확장 메커니즘은 JAR 파일 포맷에 구현된다. 확장 메커니즘으로 JAR 파일이 다른 필요한 JAR 파일들을 Class-Path 헤더를 통해 manifest 파일에 지정할 수 있다.

extension1.jar와 extension2.jar가 같은 디렉토리 안의 두 개의 JAR 파일에 있다고 가정해보자. extension1.jar의 manifest는 다음 헤더를 포함하고 있다:


Class-Path: extension2.jar

이 헤더는 extension2.jar의 클래스들이 extension1.jar의 클래스를 목표에 맞춘 확장 클래스로서 작용한다는 것을 나타내고 있다. extension1.jar의 클래스들은 extension2.jar가 클랫의 경로의 일부가 될 필요 없이 extension2.jar의 클래스를 호출할 수 있다.

JVM은 확장 메커니즘을 사용하는 JAR를 로딩할 때 Class-Path 헤더에 레퍼런스된 JAR를 클래스 경로에 자동으로 추가한다. 하지만, 확장 JAR 경로는 관련 경로로 인터프리팅되어 일반적으로 확장 JAR는 이를 레퍼런싱하는 JAR로서 같은 디렉토리에 저장되어야 한다.

예를 들어 ExtensionDemo 클래스를 레퍼런싱하는 ExtensionClient 클래스가 ExtensionClient.jar라고 하는 JAR 파일에 번들되었고 ExtensionDemo 클래스가 ExtensionDemo.jar에 번들되었다고 가정해보자. ExtensionDemo.jar가 확장으로 취급되기 위해서는 ExtensionDemo.jar는 ExtensionClient.jar의 manifest 안의 Class-Path 헤더에 리스트되어야 한다:


Manifest-Version: 1.0
Class-Path: ExtensionDemo.jar

Class-Path 헤더의 값은 경로가 지정이 안된 ExtensionDemo.jar 이며 ExtensionDemo.jar가 ExtensionClient JAR 파일과 같은 디렉토리에 위치해 있음을 나타내고 있다.

JAR 파일의 보안
JAR 파일은 jarsigner 툴을 사용하거나 java.security API를 통해서 직접 서명될 수 있다. 서명된 JAR 파일은 원래 JAR 파일과 정확히 같다. manifest만이 업데이트 된 것과 두 개의 추가 파일들이 META-INF 디렉토리에 추가된 것을 제외하고.

Keystore 데이터베이스에 저장된 인증을 사용하여 JAR 파일은 서명된다. Keystore에 저장된 인증은 패스워드로 보호된다.

그림 1. Keystore 데이터베이스
Keystore Database

JAR의 각 서명자는 JAR 파일의 META-INF 디렉토리안에 있는 .SF 확장자가 붙은 서명으로 표현된다. 이 파일의 포맷은 manifest 파일과 비슷하다. 메인 섹션과 개별 엔트리들로 구성되어 있다. 서명된 JAR에서 오는 파일을 확인하기 위해 서명 파일의 다이제스트 값은 JAR 파일의 상응 엔트리에 대비하여 계산된 다이제스트와 비교된다.

Listing 1. Manifest와 서명 파일

Contents of signature file META-INF/MANIFEST.MF

Manifest-Version: 1.0
Created-By: 1.3.0 (Sun Microsystems Inc.)

Name: Sample.java
SHA1-Digest: 3+DdYW8INICtyG8ZarHlFxX0W6g=

Name: Sample.class
SHA1-Digest: YJ5yQHBZBJ3SsTNcHJFqUkfWEmI=

Contents of signature file META-INF/JAMES.SF

Signature-Version: 1.0
SHA1-Digest-Manifest: HBstZOJBuuTJ6QMIdB90T8sjaOM=
Created-By: 1.3.0 (Sun Microsystems Inc.)

Name: Sample.java
SHA1-Digest: qipMDrkurQcKwnyIlI3Jtrnia8Q=

Name: Sample.class
SHA1-Digest: pT2DYby8QXPcCzv2NwpLxd8p4G4=

디지틀 서명
디지틀 서명은 .SF 서명 파일의 서명완료된 버전이다. 디지틀 서명 파일은 바이너리 파일이며 .SF 파일과 같은 파일이름을 갖고 있지만 다른 확장이다. 확장은 디지틀 서명 유형에 따라 다양하고 (RSA, DSA, PGP). JAR 서명에 사용된 인증 유형에 따라 다르다.

Keystore
JAR 파일에 서명하려면 프라이빗 키를 가져야 한다. 프라이빗 키와 관련 퍼블릭 키 인증은 패스워드로 보호된 데이터베이스(keystores)에 저장된다. JDK는 Keystore를 구현 및 변경하는 툴을 포함하고 있다. Keystore의 각 키는 앨리어스에 의해 구분되는데 전형적으로 키를 소유한 서명자의 이름이다.

모든 Keystore 엔트리들은 고유 앨리어스로 액세스된다. 앨리어스는 Keystore에 엔터티를 추가할 때 keytool -genkey 명령어를 사용하여 지정되어 키 쌍을 만든다. 뒤따르는 keytool 명령어는 이와 같은 앨리어스를 사용하여 엔터티를 언급해야 한다.

예를 들어 "james"라는 앨리어스로 새로운 퍼블릭/프라이빗 키 쌍을 만들고 퍼블릭 키를 자가 서명된 인증으로 래핑하려면 다음 명령어를 사용한다:


keytool -genkey -alias james -keypass jamespass 
        -validity 80 -keystore jamesKeyStore 
        -storepass jamesKeyStorePass

jarsigner 툴
jarsigner 툴은 Keystore를 사용하여 JAR 파일에 대한 디지틀 서명을 만들거나 확인한다.

위 예제에서 처럼 "jamesKeyStore" Keystore를 만들었고 여기에 "james" 앨리어스와 키를 포함하고 있다고 가정해보자. 다음 명령어로 JAR 파일에 서명할 수 있다:


jarsigner -keystore jamesKeyStore -storepass jamesKeyStorePass 
          -keypass jamespass -signedjar SSample.jar Sample.jar james

이 명령어는 앨리어스가 "james"이고 패스워드가 "jamespass"인 키를 보내 Sample.jar 파일에 서명하고 SSample.jar라는 서명된 JAR를 만든다.

jarsigner 툴은 서명된 JAR 파일을 확인할 수 있다. 이 작동은 JAR 파일을 서명하는 것 보다 훨씬 쉽다. 다음 명령어를 실행하면 된다:


jarsigner -verify SSample.jar

JAR 인덱싱(indexing)
애플리케이션 또는 애플릿이 다중의 JAR 파일들로 번들된다면 클래스 로더는 단순한 리니어 검색 알고리즘을 사용하여 클래스 경로의 엘리먼트를 검색한다. 클래스 로더가 존재하지 않은 리소스를 찾으려고 하면 애플리케이션 또는 애플릿 내의 모든 JAR 파일들은 다운로드 되어야한다. 큰 네트워크 애플리케이션과 애플릿의 경우 늦은 시작, 지연된 응답, 네트워크 대역 낭비를 초래한다.

JDK 1.3 이후 JAR 파일 포맷은 인덱싱(indexing)을 지원하여 네트워크 애플리케이션(특히 애플릿)의 클래스 검색 프로세스를 최적화했다. JarIndex 메커니즘은 애플릿 또는 애플리케이션에 정의된 모든 JAR 파일의 내용을 모아 첫 번째 JAR 파일의 인덱스 파일에 이 정보를 저장한다. 첫 번째 JAR 파일이 다운로드된 후에 애플릿 클래스 로더는 모아진 콘텐트 정보를 사용하여 JAR 파일을 효율적으로 다운로드한다. 이 디렉토리 정보는 INDEX.LIST라는 이름으로 간단한 텍스트 파일로 저장된다.(META-INF 디렉토리).

JarIndex 만들기

그림 2. JarIndex
JarIndex Demo

다음 명령어를 사용하여 JarIndex_Main.jar, JarIndex_test.jar, JarIndex_test1.jar용 인덱스 파일을 만든다:


jar -i JarIndex_Main.jar JarIndex_test.jar SampleDir/JarIndex_test1.jar 

INDEX.LIST 파일은 간단한 포맷을 갖고 있으며 색인된 JAR 파일에 저장된 패키지 또는 클래스 이름을 포함하고 있다.(Listing 2):

Listing 2. JarIndex INDEX.LIST 파일

JarIndex-Version: 1.0

JarIndex_Main.jar
sp

JarIndex_test.jar
Sample

SampleDir/JarIndex_test1.jar
org
org/apache
org/apache/xerces
org/apache/xerces/framework
org/apache/xerces/framework/xml4j

참고자료

'java core' 카테고리의 다른 글

[펌] RMI(Remote Method Invocation)  (0) 2005.05.11
jdk 5.0) In runtime, java compile.  (0) 2005.04.25
jar  (0) 2005.04.25
Runtime in jdk5.0  (0) 2005.03.26
Annotation  (0) 2005.03.18
Java condition variable  (0) 2005.02.18
Posted by 김용환 '김용환'

댓글을 달아 주세요

Runtime in jdk5.0

java core 2005. 3. 26. 04:12
Author Topic: command execution error
Marina JOSEPH
greenhorn
Member # 67293

posted March 02, 2004 01:59 AM      Profile for Marina JOSEPH   Email Marina JOSEPH   Send New Private Message      Edit/Delete Post  Reply With Quote 
import java.io.*;

class cmdEx
{
public static void main(String[] arg)
{
String str;
Process p;
BufferedReader in;
try
{
p = Runtime.getRuntime().exec("/bin/ls -aFl");
in = new BufferedReader(new InputStreamReader(p.getInputStream()));

while ((str = in.readLine()) != null)
{
System.out.println(str);
}
}
catch (IOException e)
{ System.out.println(e.toString()); }
}

}
message
c:\>java cmdEx
Error
java.io.IOException :CreateProcess :/bin/ls-aFl error=3

this can run p=Runtime.getRuntime().exec("java"); its working


but p=Runtime.getRuntime().exec("dir /p"); from windows.
again display the error
java.io.IOException :CreateProcess ir /p error=3


Posts: 18 | Registered: Feb 2004  |  IP: Logged
Corneil du Plessis
greenhorn
Member # 66649

posted March 02, 2004 03:59 AM      Profile for Corneil du Plessis   Email Corneil du Plessis   Send New Private Message      Edit/Delete Post  Reply With Quote 
In Windows you will have to use Runtime.getRuntime().exec("cmd /c dir /s")
The process you want to envoke is the standard command processor. You will notice in JDK 1.5 that System.getEnv is not deprecated anymore. It will allow you to do Runtime.getRuntime().exec(System.getEnv("ComSpec") + " /c dir /s") which will work on all Windows platforms, because you will find that cmd.exe is not available on all platforms.

Posts: 11 | Registered: Feb 2004  |  IP: Logged
Ernest Friedman-Hill
sheriff and author
Member # 52711

posted March 02, 2004 04:00 AM      Profile for Ernest Friedman-Hill   Author's Homepage   Email Ernest Friedman-Hill   Send New Private Message      Edit/Delete Post  Reply With Quote 
The UNIX code should work fine. Be sure there's really a /bin/ls on your system, and that it really accepts the flags you're giving it.

For the Windows "dir" version, note that "dir" is not a separate program, but a command implemented by cmd.exe (or command.com). I believe you need to execute something like

cmd /e "dir /p"

rather than just "dir /p".

Note that this exeact issue is covered in the Java programmer's FAQ.

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

[Jess in Action] [Weblog]


Posts: 7082 | Registered: Jul 2003  |  IP: Logged
Tim Holloway
bartender
Member # 15625

posted March 02, 2004 09:16 AM      Profile for Tim Holloway   Author's Homepage   Email Tim Holloway   Send New Private Message      Edit/Delete Post  Reply With Quote 
However this is a horrible way to get a list of files for processing. Use the java.io.File.list() method instead. You can add a filter if you like to do the equivalent of wildcards.

Hint: You can normalize filenames to the Unix style: C:\java\myfile.txt can also be accessed in java code (or most config files) as C:/java/myfile.txt.

And that way you don't get burned by forgetting that a backslash is an escape character!

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

If you extend the currently popular business philosophy to its ultimate end, we'll end up with apps produced in no time, for no cost that do nothing. And from the way some major corporate sites work, it won't be much longer.


Posts: 2800 | Registered: Jun 2001  |  IP: Logged
Ernest Friedman-Hill
sheriff and author
Member # 52711

posted March 02, 2004 09:46 AM      Profile for Ernest Friedman-Hill   Author's Homepage   Email Ernest Friedman-Hill   Send New Private Message      Edit/Delete Post  Reply With Quote 
quote:
Originally posted by Tim Holloway:
However this is a horrible way to get a list of files for processing.

I agree, but based on her other posts, she's implementing something like a homegrown rexec service; I think executing the commands is the whole point.

[ March 02, 2004: Message edited by: Ernest Friedman-Hill ]

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

[Jess in Action] [Weblog]

'java core' 카테고리의 다른 글

jdk 5.0) In runtime, java compile.  (0) 2005.04.25
jar  (0) 2005.04.25
Runtime in jdk5.0  (0) 2005.03.26
Annotation  (0) 2005.03.18
Java condition variable  (0) 2005.02.18
Threads from aritma  (0) 2005.02.12
Posted by 김용환 '김용환'

댓글을 달아 주세요

[펌] 축복송모음

scribbling 2005. 3. 24. 11:04

 



고귀한 선물
당신은 주님 주신 고귀한 선물 언제나 함께 할 내 평생의 사랑
주님과 당신께 내 삶을 드립니다 소중한 당신은 고귀한 선물
내 연약한 사랑으로 사랑할수없네 변함없는 그 신실한 사랑
오직 주님에 사랑으로 순종하며 성실하게 사랑하도록 날 도와주시네
당신은 주님주신 고귀한 선물 언제나 함께 할 내 평생의 사랑
주님과 당신께 내 삶을 드립니다 소중한 당신은 고귀한 선물
그대 향한 나의 사랑 아무런 조건없네 우리 앞길에 모진 고난이 와도
변함없이 사랑하리 우리 함께 기도하면서
모든 어려움 믿음으로 헤쳐나가리라
당신은 주님 주신 고귀한 선물 언제나 함께 할 내 평생의 사랑
주님과 당신께 삶을 드립니다 소중한 당신은 고귀한 선물
소중한 당신은 고귀한 선물



주님의 선물 - LOVE SONG

작사 : 최인혁 작곡 : 최인혁
그대는 주님 보내신 나의 가장 귀한 선물
그대는 하늘로부터 내려진 귀한선물
그대는 맑아오는 새벽인양 싱그런 사랑으로 전해오네
때론 그대 지쳐 두 눈에 눈물 지을때 그대 손잡고 주의 길 함께 하리
그대는 주님 보내신 아름다운 사랑의 편지
그대는 주님 보내신 예쁜 사랑의 하모니



이좋은날,기쁜이날에[결혼축가] - 옹기장이 Together

작사 : 유요한 작곡 : 유요한
이 좋은 날 사랑하는 두 사람 여기 머리 숙이며
주님께 함께 하심 비나이다 오늘 기쁜 이 날에
그 어려운 순간 순간 속에도 서로를 사랑하기에
두 손잡고 하나되기 원합니다. 이 좋은 날 기쁜 이날에
주여 이들과 함께 하소서 길고 긴 인생 길 갈 동안
서로 믿고 사랑하는 그 아름다움이 언제까지나 넘치도록
주여 이들과 함께 하소서 춥고 험한 세상 살 동안
서로 돕고 의지하는 그 포근한 사랑 언제까지나 타오르도록
이 좋은 날 이 기쁜 날 이 좋은 날 기쁜 이 날에 우우




믿음의 가정 - 김명식1집 영원한 사귐

우리를 죽기까지 사랑한 그 사랑으로 당신을 사랑하리
약속하신 축복의 말씀 굳게 믿으며 믿음의 가정 세우리
우리 함께 섬기며 믿음의 가정 세우리 거친 세파가 몰아칠 때
세상 사람들 모두 알게되리 믿음 위에 선 우리가정
우리의 가정은 생명으로 충만하리 은혜와 사랑으로
우리의 사랑은 희생하며 섬기는것 주님 보이신대로
우리 함께 섬기며 주님 모습 닮아가리 주님께까지 자라가리
생명의 빛을 세상에 전하리 믿음 위에 선 우리가정
우리 함께 섬기며 믿음의 가정 세우리 거친 세파도 두렵잖네
세상 사람들 모두 알게 되리 믿음 위에선 우리 가정




사랑의 노래 - 소리엘 2집

나에게 당신은 주님께서 베푸신 사랑의 노래
당신은 비추는 다가오는 해처럼 빛나는 기쁨
우리는 서로 마음으로 하나된 위로의 손길
당신은 언제나 아름다운 꽃처럼 향기를 주네
절망과 아픈 근심 우릴 흔들어도 기도는 위로와 힘이 되리니
때때로 넘어 짐은 우리 주님께서 사랑을 완성케 함이라
내 맘속에 한가지 간절한 소망은 당신과 영원히 주님 노래하는 것
언제나 항상 우리의 맘속에 주님 사랑 늘 거하시리



축가 - InCH 1집
우리 여기 모여 두분 축하 드려요
분명 두분은 주님의 사랑안에서 맺어졌죠
나는 보고있죠 주님 이 자리에서 두분으로 인해 그 얼굴
가득한 미소 지으신 것을 우린 알고있죠 주님 이가정에
많은 계획 있으신것을 우린 믿고있죠 주님 이가정에
많은 축복 내리실것을 믿고있죠
주님이 허락한 두사람을 영원히 간직하세요
주님이 우리를 이토록 사랑한 것처럼 서로 사랑하세요



축가 - 작사 : 하덕규 작곡 : 하덕규


학예회처럼 예쁜 신랑 신부에게
새로 생긴 조그만 울타리 안으로 길이나며
꽃으로 장식된 예배당이 걸어들어 왔네
그리고 둘이는 하나가 되었네
사람들 모두가 활짝 웃으며
'저는 시냇가에 심은나무'라며 노래할 때
모두들 얼마나 착해져 노래는 하늘로 올라가는지
사람들도 모두다 하나가 되었네
추웠던 전날의 아픔들 그리고 훗날에 떨어질 땀방울들도
하늘로 올라가는 꽃길이되어
아득하게 아득하게 아득하게 펼쳐졌네




아주 먼 옛날 - 김재웅


아주 먼 옛날 하늘에서는 당신을 향한 계획 있었죠
하나님께서 바라보시고 좋았더라고 말씀하셨네
이 세상 그 무엇보다 귀하게 나의 손으로 창조하였노라
내가 너로 인하여 기뻐하노라 내가 너를 사랑 하노라
사랑해요 축복해요 당신의 마음에 우리의 사랑을 드려요




사랑의 서약 - LOVE & Wedding 2집

/작사 : 조효성 작곡 : 김종훈

나 당신께 서약합니다 나의 하나님과 사람들 앞에서
당신과 함께라면 차가운 겨울도 지나 따뜻한 봄이 찾아오죠
나 당신께 서약합니다  마주잡은 손 말씀 위에 얹고
당신과 함께라면 향기로운 꽃향기와 아름다운 새소릴 듣죠
나의 사랑 어여쁜 자여 일어나 함께 걸어요
우리 이제 하나되어 죽음이 우릴 나누는 그 순간까지
나의 사랑 어여쁜 자여 일어나 함께 걸어요
이제 우리사랑으로 새로운 세상 만들어 가요




축복송 - 워킹2집-믿음(Working 2nd)

온 세상이 아름답게 피어오르고 기쁨이 가득한 이 봄날에
그대의 두손에는 장미꽃보다 사랑의 나의 마음 주고 싶어라
따스한 햇살 받아 사랑을 입고 춤추며 속삭이는 이 한날에
그대의 가슴에는 진주옷보다  진실한 나의 노래 담고 싶어라
오늘을 걸어갈 때나 내일이 밝아올 때나
오 하나님의 크신 축복 그대에게 내리리라
하루하루 살아가는 인생 속에서 꽃처럼 피어난 그대에게
두손 모아 간절하게 기도하네당신의 앞길에 축복 있으라
따스한 햇살 받아 사랑을 입고 춤추며 속삭이는 이 한날에
그대의 가슴에는 진주옷보다 진실한 나의 노래 담고 싶어라
오늘을 걸어갈 때나 내일이 밝아올 때나
오 하나님의 크신 축복 그대에게 내리리라
하루하루 살아가는 인생 속에서 꽃처럼 피어난 그대에게
두손 모아 간절하게 기도하네 당신의 앞길에 축복있으라
하나님 당신에게 축복하소서 축복하소서



두분을 축복해요 - 015B 가스펠

작사 : 조형곤 작곡 : 조형곤

주의 사랑으로 사랑합니다. 주의 사랑으로 축복합니다
주님 안에서 우린 한 형제 주의 사랑으로 축복해요 축복해요
오늘 이시간은 주님이 정해준 자리에요
천군 천사들이 모두 모여 두분을 축복해요
오늘 이 시간에 우리 함께 두분 위해 기도해요
두분의 아름다운 영혼은 하나님께 큰 영광이죠
때로는 서로의 얼굴이 지쳐보일 때가 있겠지만
서로의 따스한 사랑으로 서로를 포근히 감싸주세요
세상은 우리가 바라는대로 행복하지만은 않겠지만
두분의 진실한 믿음으로 주님과 동행하며 살아가세요




축복의 샘 - 옹기장이 7집

작사 : 곽상엽 작곡 : 곽상엽

하늘 아버지의 사랑이 놀라와 말로 표현 할 수 없는 그 사랑
함께 기쁨으로 모인 이 자리에 그 사랑으로 축복하는 이 시간
하늘 아버지의 사랑이 고마와 아주 오래 전에 계획하신 일
주의 사랑으로 하나되는 이 시간 그 말씀과 그 자비가 충만하네
주가 예비하신 가정 주님의 살과 피로 세우신 교회
축복의 샘 마르지 않네 크신 주의 사랑 갈하지 않네
주의 손이 함께 하시네  온갖 시련과 절망이 주의 손을 가려도
강한 주의 손으로 이적과 기사를 행하시네
주의 넓은 품안에 안으시네 주가 예비하신 가정
주님의 살과 피로 세우신 교회 축복의 샘 마르지 않네
크신 주의 사랑 갈하지 않네 크신 주의 사랑 갈하지 않네




축복의 걸음 - 소망의바다 2집

작사 : 민호기 작곡 : 민호기&전영훈


부르심의 한 소망 안에서하나된 우리 서로 다른 우릴한몸으로 부르셔
한 길 걷게 하시는 주의 뜻 이제 우리 같은 곳 바라보며
하나 되어서 뒤에 있는 것들을 모두 잊어버리고
푯대 되신 예수만 바라네 가야할 그 곳 지금은
비록 멀게만 느껴지지만 결국은 다다를 곳 아버지 나라
우리 더딘 걸음이지만 결코 멈추진 않죠
연약한 이 일으켜주며 함께 가야할 그 길
우리 언제까지라도 함께 할 축복의 걸음




축복의 통로 - 예수전도단(이민섭)

작사:곡 이민섭


당신은 하나님의 언약 아래 있는 축복의 통로
당신을 통하여서 열방이 주께 돌아오게 되리
당신은 하나님의 언약 아래 있는 축복의 통로
당신을 통하여서 열방이 주께 돌아오게 되리
당신은 하나님의 언약 아래 있는 축복의 통로
당신을 통하여서 열방이 주께 예배하게 되리




형제의 모습속에 보이는 - 조형섭 Over the millennium

작사 : 박정관&류형욱 작곡 : 박정관&류형욱


형제의 모습속에 보이는 하나님 형상 아름다워라
존귀한 주의 자녀 됐으니 사랑하며 섬기리
자매의 모습속에 보이는 하나님 형상 아름다워라
존귀한 주의 자녀 됐으니 사랑하며 섬기리
가끔은 상처받고 울기도 하죠 우린 연약하고 실수도 해요
우리 주님 위로의 손길 뻗치사 우리를 일으켜요
우리의 모임중에 임하신 하나님 형상 아름다워라
존귀한 왕이 여기 계시니 사랑하며 섬기리
우리 주님 오늘도 말씀 하시네요서로 몸과 같이 사랑하라고
이제부터 우리는 서로 섬겨가요 주님 하신것 같이
형제의 모습속에 보이는 하나님 형상 아름다워라
존귀한 주의 자녀 됐으니 사랑하며 섬기리
오 존귀한 왕이 여기 계시니 사랑하며 섬기리



주님의 선물 - 포스터 2집

작사 : 우상민 작곡 : 이기현

이 시간 당신을 위한 주님의 선물 드리고 싶어요
그 분은 당신을 사랑하셔서 가장 귀한 선물 주시죠
당신은 하나님 형상대로 창조된 소중한 주님의 사람
그분은 언제나 당신에게 사랑한다고 말씀하시네

당신을 향한 그 사랑은 값없이 주신 그분의 생명
주님의 사랑안에 함께 할 당신을 축복해요

Posted by 김용환 '김용환'

댓글을 달아 주세요

Annotation

java core 2005. 3. 18. 05:26

Learn to Use the New Annotation Feature of Java 5.0

Developers have always struggled to find ways of adding semantic data to their Java code. They had to: Java didn't have a native metadata facility. But that's all changed with version 5.0 of Java, which allows annotations as a typed part of the language. 
he new Java 2 Platform Standard Edition 5.0 (the developer version number is 1.5 and the code name is "Tiger") provides many new features, among them is the ability to annotate Java program elements and to create custom annotation types. Development and deployment tools can then read annotated data (also known as metadata) and process it in some fashion.

Previous versions of Java provided a limited and ad-hoc mechanism for annotating code through JavaDoc comments and keyword modifiers. Tools such as XDoclet provide a slightly more sophisticated, yet non-standard annotation syntax, which piggybacks on top of JavaDoc. But now, with Java 1.5, annotations are a typed part of the Java language and allow for both runtime and compile-time processing of annotation data.

What Are Annotations?
In short, annotations are metadata or data about data. Annotations are said to annotate a Java element. An annotation indicates that the declared element should be processed in some special way by a compiler, development tool, deployment tool, or during runtime.

Older versions of Java have rough-and-ready annotation functionality. A good example is the @deprecated JavaDoc tag. The @deprecated tag is used for more than sheer documentation purposes. This tag has no effect on the code it describes, but causes the compiler to produce warnings if any other code references the tagged element. JavaDoc does not seem to be the proper place for this type of metadata, but there was no other annotation facility in previous versions of Java. With Java 1.5, finally, annotations are a typed part of the language and the version even comes with some with pre-built annotations, one of which can be used to mark a class as deprecated (I'll cover this later).

The code below shows how you can declare a method that uses an annotation. It is one of Java 1.5's built-in annotation types:


class Child extends Parent {
    @Overrides
    public void doWork() {
        //do something      
    }
}
In the code above, note that the annotation starts with an "at" (@) sign. This annotation takes no parameters and is merely used to mark a method for some purpose. It is therefore called a marker annotation. There are also normal annotations and single member annotations (more on these later).

Annotations types are blueprints for annotations, similar to how a class is the blueprint for an object. You can create your own custom annotations by defining annotation types.

The code below shows the declaration of a normal annotation type:


public @interface MyAnnotationType {
    int someValue();
    String someOtherValue();
}
Annotations can be analyzed statically before and during compile time. Annotations will likely be used before compile time mainly to generate supporting classes or configuration files. For example, a code generator (XDoclet, for example) can use annotation data in an EJB implementation class to generate EJB interfaces and deployment descriptors for you, reducing both your effort and the error rate. The average developer will probably not be writing code-generation tools, so these annotation types are likely to be used out-of-the-box rather than authored anew.

Author's Note:

Annotations will also be used for compile-time checking such as to produce warnings and errors for different failure scenarios. An example of an annotation that is used at compile time is the new @Deprecated annotation, which acts the same as the old @deprecated JavaDoc tag. Of course, a compiler has to know how to interpret annotation data that is meant to produce compile-time warnings, so again, annotations that do things at compile time will likely be used frequently, but rarely written by average developers.

Annotations can be useful at runtime as well. Using annotations you could mark code to behave in a particular way whenever it is called. For example, you could mark some methods with a @prelog annotation. Then at runtime, you could analyze the methods that you're calling and print a particular log message before you begin executing code for that method. One way to achieve this would be through the use of the updated Java 1.5 reflection API. The reflection API now provides access to runtime-accessible annotation data.

< Another way to use annotations at runtime is to use Aspect-Oriented Programming (AOP). AOP uses pointcuts—sets of points configured to executed aspects. You could define a pointcut that will execute an aspect for an annotated method. My guess is that developers would be more likely to write their own runtime annotation types than they would annotation types used for code generation and compile-time checking. Still, writing and understanding the code that accesses the annotations (the annotation consumer) at runtime is fairly advanced.



Annotating Code
Annotations fall into three categories: normal annotations, single member annotations, and marker annotations (see Table 1). Normal and single member annotations can take member values as arguments when you annotate your code.

Category Example
Normal Annotations—Annotations that take multiple arguments. The syntax for these annotations provides the ability to pass in data for all the members defined in an annotation type. @MyNormalAnnotation(mem1="val1", mem2="val2") public void someMethod() { ... }
Single Member Annotations—An annotation that only takes a single argument has a more compact syntax. You don't need to provide the member name. @MySingleMemberAnnotation("a single value") public class SomeClass { ... }
Marker Annotations—These annotations take no parameters. They are used to mark a Java element to be processed in a particular way. @Deprecated public void doWork() { ... }

Any Java declaration can be marked with an annotation. That is, an annotation can be used on a: package, class, interface, field, method, parameter, constructor, enum (newly available in Java 1.5), or local variable. An annotation can even annotate another annotation. Such annotations are called meta-annotations.

This code below shows an annotated class, constructor, field, and method.


@ClassLevelAnnotation(arg1="val1", arg2={"arg2.val1","arg2.val2"})
public class AnnotationExample {

    @FieldLevelAnnotation()
    public String field;    

    @CtorLevelAnnotation()
    public AnnotationsTest() {
        // code
    }

    @MethodLevelAnnotationA("val")
    @MethodLevelAnnotationB(arg1="val1",arg2="val2")
    public void someMethod(String string) {
        // code
    }    
}
As you can see in the ClassLevelAnnotation, annotations may also take arrays of values. When creating an annotation that requires an array you must surround the array of parameters with brackets ( { … } ) and you must comma-separate each array parameter.

Annotation types may define default values for some members. Every member-value pair that does not have a default value must be supplied when you create an annotation.

The annotation-type declaration below uses meta-annotations, which provide information on how the annotation type can be used:


@Retention(RUNTIME) 
@Target(METHOD)
public @interface MyAnnotationType {
    String value;
}
This code shows how to use meta-annotations when declaring your own annotation type. I will cover the specifics on declaring annotation types and meta-annotation types later, but for now, just know that you can use meta-annotations to annotate other annotations.

Packages annotations are also allowed, but because packages are not explicitly declared in Java, package annotations must be declared in a source file called package-info.java in the directory containing the source files for the package. This file should contain only a package declaration, preceded by any annotations that should apply to the package. This java file must not contain an actual class definition (which would be illegal anyways, because package-info is not a legal identifier).



Built-in Annotations
Java 1.5 comes packaged with seven pre-built annotations. I will describe these built-in annotations in this section, borrowing much of the wording directly from the Java 5 documentation.

java.lang.Override


@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
This annotation is used to indicate that a method declaration is intended to override a method declaration in a superclass. If a method is annotated with this annotation type but does not override a superclass method, compilers are required to generate an error message.

This annotation is useful in avoiding the situation where you think you are overriding a method, but you misspell the method name in the child class. The code in the method you are trying to override in the parent class will be used because the name is misspelled in the child class. This is usually a difficult bug to track down because no compiler or runtime error is thrown. But marking a method with the Override annotation would help you realize this type of problem at compile time rather than through arduous debugging.

java.lang.Deprecated


@Documented
@Retention(value=RUNTIME)
public @interface Deprecated
A program element annotated @Deprecated is one that programmers are discouraged from using, typically because it is dangerous or because a better alternative exists. Using this annotation, compilers warn when a deprecated program element is used or overridden in non-deprecated code.

java.lang.SuppressWarning


@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
This annotation indicates that the named compiler warnings should be suppressed in the annotated element (and in all program elements contained in the annotated element). Note that the set of warnings suppressed in a given element is a superset of the warnings suppressed in all containing elements. For example, if you annotate a class to suppress one warning and annotate a method to suppress another, both warnings will be suppressed in the method.

java.lang.annotation.Documented


@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Documented
Annotations with a type declaration are to be documented by javadoc and similar tools by default. It should be used to annotate the declarations of types whose annotations affect the use of annotated elements by their clients. If a type declaration is annotated with @Documented, its annotations become part of the public API of the annotated elements.

java.lang.annotation.Inherited


@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited
This indicates that an annotation type is automatically inherited. If an inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found or the top of the class hierarchy (Object) is reached. If no superclass has an annotation for this type, then the query will indicate that the class in question has no such annotation.

Note that this meta-annotation type has no effect if the annotated type is used for anything other than a class. Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect.

java.lang.annotation.Retention


@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Retention
Retention takes a single value of the enumerated type RetentionPolicy, which informs the compiler of its policy for retaining annotations in memory. There are three RetentionPolicy enumerated values:
  • SOURCE—Annotations are to be discarded by the compiler.
  • CLASS—Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at runtime. This is the default behavior.
  • RUNTIME—Annotations are to be recorded in the class file by the compiler and retained by the VM at runtime, so they may be read reflectively.

If no Retention annotation is present on an annotation type declaration, the retention policy defaults to RetentionPolicy.CLASS. By default, the annotation data is not available in the JVM in order to reduce overhead.

java.lang.annotation.Target


@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Target
@Target indicates the kinds of program element to which an annotation type is applicable. It takes a single enumerated parameter of type ElementType. The enumerated values are as follows:
  • TYPE (class, interface or enum declaration)
  • FIELD (includes enum constants)
  • METHOD
  • PARAMETER
  • CONSTRUCTOR
  • LOCAL_VARIABLE
  • ANNOTATION_TYPE
  • PACKAGE
If a @Target meta-annotation is not present on an annotation type declaration, the declared type may be used on any program element. If such a meta-annotation is present, the compiler will enforce the specified usage restriction.



Declaring Annotation Types
Now that you've learned a little about the annotations that come packaged with Java 1.5, you can move on to declaring your own annotation types.

Here is a sample annotation type:


public @interface MyAnnotationType {
    int someValue();
    String someOtherValue();
    String yesSomeOtherValue() default "[blank]";

}
As you can see, annotation types are declared similarly to interfaces. In fact they use the interface keyword prepended with an @ sign. Just like an interface, annotations have method declarations, each of which defines a member of the annotation. These methods may not have any parameters. As shown in the example, default values are specified after the parenthesis on a method declaration.

At first, it seems a little odd to specify the members of an annotation using method syntax, because when you declare a Java element and annotate it (e.g. when you annotate a class), you may pass values into the annotation. But you are not the consumer of the annotation at this point; you are merely constructing an instance of the annotation. Think of it as calling a constructor on an implicit Java class that you didn't have to write.

The annotation consumers are the development tools, the compiler, or a runtime library that accesses the annotation data you created when you annotated your Java code. After you've created the annotation, annotation consumers may call the methods on the annotation interface in order to get the annotation values. The return types of the methods represent the type of data that a consumer of the annotation would get back.

There are a few restrictions when defining annotation types.

  • Annotations cannot extend other annotations, except for the java.lang.annotation.Annotation marker interface they inherently extend.
  • Method return types must be: primitive types, String, Class, enum types, annotation types, or arrays of the preceding types.
  • Annotations can't have a throws clause.
  • Self reference is not allowed (e.g. AnnotationA contains a member of type AnnotationA), nor circular reference (e.g. AnnotationA contains a member of type AnnotationB and vice versa).
  • Single member annotations must define only a single method called value in the annotation type. So long as you follow this construct, you can use the condensed single-member syntax when creating an annotation of this type.

    A single member annotation can be defined as follows:
    
    public @interface Copyright {
        String value();
    }
    
You can also create meta-annotations, as we have already seen. A meta-annotation is any annotation that can be used to annotate other annotations. For example, all of the build-in annotations utilize meta-annotations and some of them are meta-annotations themselves.

Here is the declaration for the @ Inherited meta-annotation type:


@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited
The @Inherited annotation uses three other meta-annotations and is a meta-annotation itself. What makes it a meta-annotation? Any annotation whose @Target annotation member includes ANNOTATION_TYPE can be used as a meta-annotation.

Reading Annotations
Now that you know how to declare your own annotation types and annotate Java elements, you need to know how to access the annotation data you specified. Annotation consumers are software that read annotation data and do something useful with it. Annotation consumers fall into three groups:

General tools are programs that can analyze source code and do something useful with it. For example compilers and documentation generators are both considered general tools. General tools do not load annotated classes or annotation interfaces into the virtual machine.

Specific tools are also programs that can analyze source code without loading annotated classes, but they need to load annotation interfaces into the virtual machine. An example of this is a Stub generator.

Introspectors are programs that can query their own annotations (the ones with a RUNTIME retention policy). Introspectors will load both annotated classes and annotation interfaces into the virtual machine.

Listing 1 is an example of how you can access your code during runtime using the reflection API.

As I mentioned previously, I think that most developers will be users of annotations, but few will have to write code that consumes annotations.

If you are a general or specific tools developer, you may want to look into a few APIs that will help you read annotations from source files in order to use them for some type of static preprocessing. The new Doclet API (com.sun.javadoc) has support for reading annotations. It doesn’t currently support all of the features of annotations, but it may suffice as a library for writing your tool. Another thing to keep an eye on is the newly submitted JSR-269 (Pluggable Annotation Processing API), which will provide a generic API that allows developers to read annotation data using general and specific tools.

If you are developing an introspector, you can use the new reflection API to access your annotations, though I would suggest looking into AOP as a means to interface with annotated methods. I think there is a lot of room for exploration here. The combinations of aspects and annotations can yield a very powerful introspection tool.

Javid Jamae consults for Valtech, a global consulting group specializing in delivering advanced technology solutions. Valtech endeavors to help its customers through its global delivery model to create and/or maintain an affordable competitive advantage.

'java core' 카테고리의 다른 글

jar  (0) 2005.04.25
Runtime in jdk5.0  (0) 2005.03.26
Annotation  (0) 2005.03.18
Java condition variable  (0) 2005.02.18
Threads from aritma  (0) 2005.02.12
Reference object model from java world  (0) 2005.01.28
Posted by 김용환 '김용환'

댓글을 달아 주세요

(문제)

1~4값을 입력받아 procA~procD함수를 호출하는 함수를 작성하시오.

 

#define n_A (0)

#define n_B (1)

#define n_C (2)

#define n_D (3)

 

static void sub(int cmd)

{

    switch(cmd)

    {  case n_A : procA( ); break;

        case n_B : procB( ); break;

        case n_C : procC( ); break;

        case n_D : procD( ); break;

     }

}

 

=>안정성이 아니라, 효율성때문에 함수포인터와 enum을 쓰는 겁니다. 일반적으로 상수의 범위가 작기 때문에 전처리기에서 상수를 처리하면, 용량을 많이 차지 할수 있는 부분이 있는 겁니다. 그래서 간단히 상수만을 선언하고 사용하기 위해서, enum을 쓰는게 훨씬 메모리를 아낄수 있는 부분입니다.

어떤 일을 비슷한 범위에서 하는데, 같은 함수를 정의하는게 상당히 좋지 않을 수 있습니다.

코드의 낭비를 줄이기 위해서 씁니다. 그래서 함수포인터는 쓰는 거지요. 임베디드 시스템에서는 주로 이렇게 쓰죠..

 

 

 

typedef enum

{  n_A = 0,

    n_B,

    n_C,

    n_D

};

 

static void sub(int cmd)

{

     static const struct t_convtbl

     {    int val;

           BOOL (* proc)(void);

      } convtbl[ ] = { { n_A, procA },

                            { n_B, procB },

                            { n_C, procC },

                            { n_D, procD }};

      if ((sizeof(convtbl)/sizeof(convtbl[0]))>cmd )

     {

           (*convtbl[cmd].proc)();

     }

}

 

 

펌) http://cafe.naver.com/cafec.cafe

'c or linux' 카테고리의 다른 글

[펌] Visual Studio 6.0의 라이브러리 링크  (0) 2005.11.05
c programming FAQs  (0) 2005.06.23
enumeration + function pointer  (0) 2005.03.16
define 문에서 do while 문 사용이유  (0) 2005.03.15
상수 포인터  (0) 2005.03.15
library  (0) 2005.03.13
Posted by 김용환 '김용환'

댓글을 달아 주세요

http://bbs.kldp.org/viewtopic.php?t=19289&highlight=while%280%29

 

<linux/module.h>에 보면 아래와 같은 매크로가 있습니다.

코드:
#ifdef CONFIG_MODULES
#define SET_MODULE_OWNER(some_struct) do { (some_struct)->owner = THIS_MODULE } while (0)
#else
#define SET_MODULE_OWNER(some_struct) do { } while (0)
#endif


 

왜 do while문을 쓰는 이유는??

 

 

소스: http://www.kernelnewbies.org/faq/

Why do a lot of #defines in the kernel use do { ... } while(0)?

There are a couple of reasons:

* (from Dave Miller) Empty statements give a warning from the compiler so this is why you see #define FOO do { } while(0).
* (from Dave Miller) It gives you a basic block in which to declare local variables.
* (from Ben Collins) It allows you to use more complex macros in conditional code. Imagine a macro of several lines of code like:

#define FOO(x) \
printf("arg is %s\n", x); \
do_something_useful(x);

Now imagine using it like:

if (blah == 2)
FOO(blah);

This interprets to:

if (blah == 2)
printf("arg is %s\n", blah);
do_something_useful(blah);;

As you can see, the if then only encompasses the printf(), and the do_something_useful() call is unconditional (not within the scope of the if), like you wanted it. So, by using a block like do{...}while(0), you would get this:

if (blah == 2)
do {
printf("arg is %s\n", blah);
do_something_useful(blah);
} while (0);

Which is exactly what you want.
* (from Per Persson) As both Miller and Collins point out, you want a block statement so you can have several lines of code and declare local variables. But then the natural thing would be to just use for example:

#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }

However that wouldn't work in some cases. The following code is meant to be an if-statement with two branches:

if(x>y)
exch(x,y); // Branch 1
else
do_something(); // Branch 2

But it would be interpreted as an if-statement with only one branch:

if(x>y) { // Single-branch if-statement!!!
int tmp; // The one and only branch consists
tmp = x; // of the block.
x = y;
y = tmp;
}
; // empty statement
else // ERROR!!! "parse error before else"
do_something();

The problem is the semi-colon (Wink coming directly after the block.

The solution for this is to sandwich the block between do and while(0). Then we have a single statement with the capabilities of a block, but not considered as being a block statement by the compiler.

Our if-statement now becomes:

if(x>y)
do {
int tmp;
tmp = x;
x = y;
y = tmp;
} while(0);
else
do_something();

'c or linux' 카테고리의 다른 글

c programming FAQs  (0) 2005.06.23
enumeration + function pointer  (0) 2005.03.16
define 문에서 do while 문 사용이유  (0) 2005.03.15
상수 포인터  (0) 2005.03.15
library  (0) 2005.03.13
[펌] 이중 포인터 사용 용도 질문(내공있음)  (0) 2005.03.12
Posted by 김용환 '김용환'

댓글을 달아 주세요

상수 포인터

c or linux 2005. 3. 15. 19:15

void test1( char const * const x )
{
   x++;    // No. 1
   (*x)++;   // No. 2
   return;
}

 

void test2( char const *y )
{
   y++;      // No. 3
   (*y)++;   // No. 4
   return;
}

 

void test3( char * const z )
{
   z++;      // No. 5
   (*z)++;   // No. 6
   return;
}

 

void main( void )
{
    char x;
    test1( &x );
    test2( &x );
    test3( &x );
}

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

1,2,3,6으로 생각했었는데...아니군요...전 지금까지 const에 대해 잘못 알고있었군요...에고...큰일날뻔했네요...

 

'const data_type variable_name' 으로만 해왔었는데, 가끔 'data_type const variable_name'형식으로 되어있는걸 보면 왜 보기 불편하게 이렇게 해놓나 생각했었는데 그 포인터의 경우 그 위치에 따라 결정되는 군요.

 

찾아봤더니 C99엔 다음과 같이 친절히 설명이 되어있더군요. 친절한 설명도 영어의 압박이 '불친절'의 누명을 씌웠지만요^^

 

1) The type designated as "float *" has type "pointer to float". Its type category is pointer, not a floating type. The const-qualified version of this type is designated as "float * const" whereas the type designated as "const float *" is not a qualified type — its type is "pointer to constqualified float" and is a pointer to a qualified type.

 

2) The following pair of declarations demonstrates the difference between a "variable pointer to a constant value" and a "constant pointer to a variable value".


const int *ptr_to_constant;
int *const constant_ptr;


 

The contents of any object pointed to by ptr_to_constant shall not be modified through that pointer, but ptr_to_constant itself may be changed to point to another object. Similarly, the contents of the int pointed to by constant_ptr may be modified, but  constant_ptr itself shall always point to the same location.

 

1) "float *"형은 float의 pointer형으로 명시된다. 이 타입은 float형이 아니라 포인터에 속한다. 이 데이터 타입(float의 pointer형)의 상수형은 "const float *"가 아닌 "float * const"로 명시된다. "const flaot *"은 상수로 제한된 float의 포인터이다.

 

2) 다음 두 쌍의 선언문은 "상수의 포인터변수"와 "변수의 포인터상수"의 다름을 설명한다.

 

const int *ptr_to_constant;
int *const constant_ptr;

 

ptr_to_constant(상수를 가리키는 포인터변수)에 의해 접근되는 어떤 개체의 요소는 그 포인터에 의해 변경될 수 없다. 하지만  ptr_to_constant 자체는 다른 개체를 접근할 수 있도록 변경될 수 있다. 이와 반대로, constant_ptr(포인터상수)가 가리키는 개체는 수정될 것이다. 하지만 constant_ptr 자체는 항상 같은 위치(메모리 상의 주소)를 가리킬것이다.

Posted by 김용환 '김용환'

댓글을 달아 주세요

library

c or linux 2005. 3. 13. 04:15

library 의 사용

윤 상배

yundream@coconut.co.kr



1절. 소개

이 문서는 library 의 사용방법에 대한 내용을 담고 있다. 왜 라이브러리가 필요한지, 라이브러리는 어떤 종류가 있으며, 어떻게 작성할수 있는지, 그리고 어떻게 사용하는지에 대해서 얘기하도록 할것이다. 그리고 중간중간에 이해를 돕기 위한 실제 코딩역시 들어갈 것이다.

라이브러리에 대한 이러저러한 세부적인 내용까지 다루진 않을것이다. 좀더 이론적인 내용을 필요로 한다면 Program Library HOWTO 를 참고하기 바란다. 이 문서에서는 라이브러리를 만들고 활용하는 면에 중점을 둘것이다. 그러므로 위의 문서는 이문서를 읽기전에 대충이라도 한번 읽어보도록 한다.

정적 라이브러리와 공유라이브러리는 일반적인 내용임으로 간단한 설명과 일반적인 예제를 드는 정도로 넘어갈 것이다. 그러나 동적라이브러리에 대해서는 몇가지 다루어야할 이슈들이 있음으로 다른 것들에 비해서 좀더 비중있게 다루게 될것이다.


2절. Library 이야기

2.1절. 라이브러리란 무엇인가

라이브러리란 특정한 코드(함수 혹은 클래스)를 포함하고 있는 컴파일된 파일이다. 이러한 라이브러리를 만드는 이유는 자주 사용되는 특정한 기능을 main 함수에서 분리시켜 놓음으로써, 프로그램을 유지, 디버깅을 쉽게하고 컴파일 시간을 좀더 빠르게 할수 있기 때문이다.

만약 라이브러리를 만들지 않고 모든 함수를 main 에 집어 넣는다면, 수정할때 마다 main 코드를 수정해야 하고 다시 컴파일 해야 할것이다. 당연히 수정하기도 어렵고 컴파일에도 많은 시간이 걸린다.

반면 라이브러리화 해두면 우리는 해당 라이브러리만 다시 컴파일 시켜서 main 함수와 링크 시켜주면 된다. 시간도 아낄뿐더러 수정하기도 매우 쉽다.


2.2절. 라이브러리의 종류

라이브러리에도 그 쓰임새에 따라서 여러가지 종류가 있다(크게 3가지). 가장 흔하게 쓰일수 있는 "정적라이브러리"와 "공유라이브러리", "동적라이브러리" 가 있다.

이들 라이브러리가 서로 구분되어지는 특징은 적재 시간이 될것이다.

정적라이브러리

정적라이브러리는 object file(.o로 끝나는) 의 단순한 모음이다. 정적라이브러린느 보통 .a 의 확장자를 가진다. 간단히 사용할수 있다. 컴파일시 적재되므로 유연성이 떨어진다. 최근에는 정적라이브러리는 지양되고 있는 추세이다. 컴파일시 적재되므로 아무래도 바이너리크기가 약간 커지는 문제가 있을것이다.

공유라이브러리

공유라이브러리는 프로그램이 시작될때 적재된다. 만약 하나의 프로그램이 실행되어서 공유라이브러리를 사용했다면, 그뒤에 공유라이브러리를 사용하는 모든 프로그램은 자동적으로 만들어져 있는 공유라이브러리를 사용하게 된다. 그럼으로써 우리는 좀더 유연한 프로그램을 만들수 잇게 된다.

정적라이브러리와 달리 라이브러리가 컴파일시 적재되지 않으므로 프로그램의 사이즈 자체는 작아지지만 이론상으로 봤을때, 라이브러리를 적재하는 시간이 필요할것이므로 정적라이브러리를 사용한 프로그램보다는 1-5% 정도 느려질수 있다. 하지만 보통은 이러한 느림을 느낄수는 없을것이다.

동적라이브러리

공유라이브러리가 프로그램이 시작될때 적재되는 반면 이것은 프로그램시작중 특정한때에 적재되는 라이브러리이다. 플러그인 모듈등을 구현할때 적합하다. 설정파일등에 읽어들인 라이브러리를 등록시키고 원하는 라이브러리를 실행시키게 하는등의 매우 유연하게 작동하는 프로그램을 만들고자 할때 유용하다.


2.2.1절. 왜 정적라이브러리의 사용을 지양하는가

예전에 libz 라는 라이브러리에 보안 문제가 생겨서 한창 시끄러웠던적이 있다. libz 라이브러리는 각종 서버프로그램에 매우 널리 사용되는 라이브러리였는데, 실제 문제가 되었던 이유는 많은 libz 를 사용하는 프로그램들이 "정적라이브러리" 형식으로 라이브러리를 사용했기 때문에, 버그픽스(bug fix)를 위해서는 문제가 되는 libz 를 사용하는 프로그램들을 다시 컴파일 시켜야 했기 때문이다. 한마디로 버그픽스 자체가 어려웠던게 큰 문제였었다. 도대체 이 프로그램들이 libz 를 사용하고 있는지 그렇지 않은지를 완전하게 알기도 힘들뿐더러, 언제 그많은 프로그램을 다시 컴파일 한단 말인가.

만약 libz 를 정적으로 사용하지 않고 "공유라이브러리" 형태로 사용한다면 bug fix 가 훨씬 쉬웠을것이다. 왜냐면 libz 공유라이브러리는 하나만 있을 것이므로 이것만 업그레이드 시켜주면 되기 때문이다.

아뭏든 이렇게 유연성이 지나치게 떨어진다는 측면이 정적라이브러리를 사용하지 않는 가장 큰 이유가 될것이다. 프로그램들의 덩치가 커지는 문제는 유연성 문제에 비하면 그리큰문제가 되지는 않을것이다.


3절. 라이브러리 만들고 사용하기

이번장에서는 실제로 라이브러리를 만들고 사용하는 방법에 대해서 각 라이브러리 종류별로 알아볼 것이다.


3.1절. 라이브러리화 할 코드

라이브러리의 이름은 libmysum 이 될것이며, 여기에는 2개의 함수가 들어갈 것이다. 하나는 덧셈을 할 함수로 "ysum" 또 하나는 뺄셈을 위한 함수로 "ydiff" 으로 할것이다. 이 라이브러리를 만들기 위해서 mysum.h 와 mysum.c 2개의 파일이 만들어질것이다.

mysum.h

int ysum(int a, int b); 
int ydiff(int a, int b);
			

mysun.c

#include "mysum.h"
int ysum(int a, int b)
{
    return a + b; 
}
int ydiff(int a, int b)
{
    return a - b;
}
			


3.2절. 정적라이브러리 제작

정적라이브러리는 위에서 말했듯이 단순히 오브젝트(.o)들의 모임이다. 오브젝트를 만든다음에 ar 이라는 명령을 이용해서 라이브러리 아카이브를 만들면 된다.

[root@localhost test]# gcc -c mysum.c
[root@localhost test]# ar rc libmysum.a mysum.o
			
아주아주 간단하다. 단지 ar 에 몇가지 옵션만을 이용해서 libmysum 이란 라이 브러리를 만들었다. 'r' 은 libmysum.a 라는 라이브러리 아카이브에 새로운 오브젝트를 추가할것이라는 옵션이다. 'c' 는 아카이브가 존재하지 않을경우 생성하라는 옵션이다.

이제 라이브러리가 실제로 사용가능한지 테스트해보도록 하자.

예제 : print_sum.c

#include "mysum.h"
#include <stdio.h>
#include <string.h>

int main()
{
    char oper[5];
    char left[11];
    char right[11];
    int  result;

    memset(left, 0x00, 11);
    memset(right, 0x00, 11);

    // 표준입력(키보드)으로 부터  문자를 입력받는다.
    // 100+20, 100-20 과 같이 연산자와 피연산자 사이에 공백을 두지 않아야 한다.  
    fscanf(stdin, "%[0-9]%[^0-9]%[0-9]", left, oper, right);
    if (oper[0] == '-')
    {
        printf("%s %s %s = %d\n", left, oper, right, 
                        ydiff(atoi(left), atoi(right)));
    }
    if (oper[0] == '+')
    {
        printf("%s %s %s = %d\n", left, oper, right, 
                        ysum(atoi(left), atoi(right)));
    }
}
			

위의 프로그램을 컴파일 하기 위해서는 라이브러리의 위치와 어떤 라이브러리를 사용할것인지를 알려줘야 한다. 라이브러리의 위치는 '-L' 옵션을 이용해서 알려줄수 있으며, '-l' 옵션을 이용해서 어떤 라이브러리를 사용할것인지를 알려줄수 있다. -l 뒤에 사용될 라이브러리 이름은 라이브러리의 이름에서 "lib"와 확장자 "a"를 제외한 나머지 이름이다. 즉 libmysum.a 를 사용할 것이라면 "-lmysum" 이 될것이다.

[root@localhost test]# gcc -o print_sum print_num.c -L./ -lmysum
			
만약 우리가 사용할 라이브러리가 표준 라이브러리 디렉토리경로에 있다면 -L 을 사용하지 않아도 된다. 표준라이브러리 디렉토리 경로는 /etc/ld.so.conf 에 명시되어 있다.

정적라이브러리 상태로 컴파일한 프로그램의 경우 컴파일시에 라이브러리가 포함되므로 라이브러리를 함께 배포할 필요는 없다.


3.3절. 공유라이브러리 제작 / 사용

print_sum.c 가 컴파일되기 위해서 사용할 라이브러리 형태가 정적라이브러리에서 공유라이브러리로 바뀌였다고 해서 print_sum.c 의 코드가 변경되는건 아니다. 컴파일 방법역시 동일하며 단지 라이브러리 제작방법에 있어서만 차이가 날뿐이다.

이제 위의 mysum.c 를 공유라이브러리 형태로 만들어보자. 공유라이브러리는 보통 .so 의 확장자를 가진다.

[root@localhost test]# gcc -fPIC -c mysum.c
[root@localhost test]# gcc -shared -W1,-soname,libmysutff.so.1 -o libmysum.so.1.0.1 mysum.o
[root@localhost test]# cp libmysum.so.1.0.1 /usr/local/lib
[root@localhost test]# ln -s /usr/local/lib/libmysum.so.1.0.1 /usr/local/lib/libmysum.so
			
우선 mysum.c 를 -fPIC 옵션을 주어서 오브젝트 파일을 만들고, 다시 gcc 를 이용해서 공유라이브러리를 제작한다. 만들어진 라이브러리를 적당한 위치로 옮기고 나서 ln 을 이용해서 컴파일러에서 인식할수 있는 이름으로 심볼릭 링크를 걸어준다.

컴파일 방법은 정적라이브러리를 이용한 코드의 컴파일 방법과 동일하다.

[root@coco test]# gcc -o print_sum print_sum.c -L/usr/local/lib -lmysum
			

공유라이브러리는 실행시에 라이브러리를 적재함으로 프로그램을 배포할때는 공유라이브러리도 함께 배포되어야 한다. 그렇지 않을경우 다음과 같이 공유라이브러리를 찾을수 없다는 메시지를 출력하면서 프로그램 실행이 중단될 것이다.

[root@coco library]# ./print_sum
./print_sum: error while loading shared libraries: libmysub.so: cannot open shared object file: No such file or directory
			
위와 같은 오류메시지를 발견했다면 libmysub.so 가 시스템에 존재하는지 확인해 보자. 만약 존재하는데도 위와 같은 오류가 발생한다면 이는 LD_LIBRARY_PATH 나 /etc/ld.so.conf 에 라이브러리 패스가 지정되어 있지 않을 경우이다. 이럴때는 LD_LIBRARY_PATH 환경변수에 libmysub.so 가 있는 디렉토리를 명시해주거나, /etc/ld.so.conf 에 디렉토리를 추가시켜주면 된다.

만약 libmysub.so 가 /usr/my/lib 에 복사되어 있고 환경변수를 통해서 라이브러리의 위치를 알려주고자 할때는 아래와 같이 하면된다.

[root@localhost test]# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/my/lib 
			
그렇지 않고 ld.so.conf 파일을 변경하길 원한다면(이럴경우 관리자 권한을 가지고 있어야 할것이다) ld.so.conf 에 라이브러리 디렉토리를 추가하고 ldconfig 를 한번 실행시켜주면 된다.
[root@localhost test]# cat /usr/my/lib >> /etc/ld.so.conf 
[root@localhost test]# ldconfig
			
ldconfig 를 실행시키게 되면 /etc/ld.so.conf 의 파일을 참조하여서 /etc/ld.so.cache 파일이 만들어지고, 프로그램은 ld.so.cache 의 디렉토리 경로에서 해당 라이브러리가 있는지 찾게 된다.


3.4절. 동적라이브러리의 사용

동적라이브러리라고 해서 동적라이브러리를 만들기 위한 어떤 특별한 방법이 있는것은 아니다. 일반 공유라이브러리를 그대로 쓰며, 단지 실행시간에 동적라이브러리를 호출하기 위한 방법상의 차이만 존재할 뿐이다.

정적/공유 라이브러리가 라이브러리의 생성방법과 컴파일방법에 약간의 차이만 있고 코드는 동일하게 사용되었던것과는 달리 동적라이브러리는 코드자체에 차이가 있다. 그럴수밖에 없는게, 동적라이브러리는 프로그램이 샐행되는 중에 특정한 시점에서 부르고 싶을때 라이브러리를 적재해야 하므로, 라이브러리를 적재하고, 사용하고 해제(free) 하기 위한 코드를 생성해야 하기 때문이다.

linux 에서는 이러한 라이브러리를 호출하기 위한 아래와 같은 함수들을 제공한다. 아래의 함수들은 solaris 에서 동일하게 사용될수 있다.

#include <dlfcn.h>

void *dlopen (const char *filename, int flag);
const char *dlerror(void);
void *dlsym(void *handle, char *symbol);
int dlclose(void *handle); 
			
dlopen 은 동적라이브러리를 적재하기 위해서 사용된다. 첫번째 아규먼트인 filename 은 /usr/my/lib/libmysum.so 와 같이 적재하기 원하는 라이브러리의 이름이다. 만약 적재시킬 라이브러리의 이름이 절대경로로 지정되어 있지 않을경우에는 LD_LIBRARY_PATH 에 등록된 디렉토리에서 찾고, 여기에서도 찾지 못할경우 /etc/ld.so.cache 에 등록된 디렉토리 리스트에서 찾게 된다. dlopen 이 성공적으로 호출되면 해당 라이브러리에 대한 handle 값을 넘겨 준다. flag 는 RTLD_LAZY와 RTLD_NOW 중 하나를 정의할수 있다. RTLD_LAZY는 라이브러리의 코드가 실행시간에 정의되지 않은 심볼을 해결하며, RTLD_NOW 는 dlopen 의 실행이 끝나기전에(return 전에) 라이브러리에 정의되지 않은 심볼을 해결한다.

dlerror 는 dl 관련함수들이 제대로 작동을 수행하지 않았을경우 에러메시지를 되돌려준다. dleooro(), dlsym(), dlclose(), dlopen()중 마지막 호출된 함수의 에러메시지를 되돌려준다.

dlsym 은 dlopen 을 통해서 열린라이브러리를 사용할수 있도록 심볼값을 찾아준다. 심볼이라고 하면 좀 애매한데, 심볼값은 즉 열린라이브러리에서 여러분이 실제로 호출할 함수의이름이라고 생각하면 된다. handle 는 dlopen 에 의해서 반환된 값이다. symbol 은 열린라이브러리에서 여러분이 실제로 부르게될 함수의 이름이다. dlsym 의 리턴값은 dlopen 으로 열린 라이브러리의 호출함수를 가르키게 된다. 리턴값을 보면 void * 형으로 되어 있는데, void 형을 사용하지 말고 호출함수가 리턴하는 형을 직접명시하도록 하자. 이렇게 함으로써 나중에 프로그램을 유지보수가 좀더 수월해진다.


3.5절. 동적라이브러리를 사용하여 프로그램의 확장성과 유연성을 높이기

동적라이브러리는 실행시간에 필요한 라이브러리를 호출할수 있음으로 조금만(사실은 아주 많이겠지만 T.T) 신경쓴다면 매우 확장성높고 유연한 프로그램을 만들수 있다.

동적라이브러리의 가장 대표적인 예가 아마도 Plug-in 이 아닐까 싶다. 만약에 모질라 브라우저가 plug-in 을 지원하지 않는 다면 우리는 새로운 기능들 이 추가될때 마다 브라우저를 다시 코딩하고 컴파일하는 수고를 해야할것이다. 그러나 동적라이브러리를 사용하면 브라우저를 다시 코딩하고 컴파일 할필요 없이, 해당 기능을 지원하는 라이브러리 파일만 받아서 특정 디렉토리에 설치하기만 하면 될것이다. 물론 동적라이브러리를 사용하기만 한다고 해서 이러한 기능이 바로 구현되는 건 아니다. Plug-in 의 효율적인 구성을 위한 표준화된 API를 제공하고 여기에 맞게 Plug-in 용 라이브러리를 제작해야만 할것이다.

우리가 지금까지 얘로든 프로그램을 보면 현재 '+', '-' 연산을 지원하고 있는데, 만약 'x', '/' 연산을 지원하는 라이브러리가 만들어졌다면, 우리는 프로그램의 코딩을 다시해야만 할것이다. 이번에는 동적라이브러리를 이용해서 plug-in 방식의 확장이 가능하도록 프로그램을 다시 만들어 보도록 할것이다.


3.5.1절. 동적라이브러리를 이용한 예제

동적라이브러리를 이용해서 main 프로그램의 재코딩 없이 추가되는 새로운 기능을 추가시키기 위해서는 통일된 인터페이스를 지니는 특정한 형식을 가지도록 라이브러리가 작성되어야 하며, 설정파일을 통하여서 어떤 라이브러리가 불리어져야 하는지에 대한 정보를 읽어들일수 있어야 한다. 그래서 어떤 기능을 추가시키고자 한다면 특정 형식에 맞도록 라이브러리를 제작하고, 설정파일을 변경하는 정도로 만들어진 새로운 라이브러리의 기능을 이용할수 있어야 한다.

설정파일은 다음과 같은 형식으로 만들어진다. 설정파일의 이름은 plugin.cfg 라고 정했다.

+,ysum,libmysum.so
-,ydiff,libmysum.so
				
'-' 연산에대해서는 libmysum.so 라이브러리를 호출하며, ydiff 함수를 사용한다. '=' 연산에 대해서는 libmysum.so 라이브러리를 호출하고 ysum 함수를 사용한다는 뜻이다. 설정파일의 이름은 plugin.cfg 로 하기로 하겠다.

다음은 동적라이브러리로 만들어진 print_sum 의 새로운 버젼이다.

예제 : print_sum_dl.c

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>

struct input_data
{
    char    oper[2];
    char    func[10]; 
    char    lib[30];
};

int main(int argc, char **argv)
{
    char oper[2];
    char left[11];
    char right[11];
    char buf[50];
    char null[1];
    int data_num;

    struct input_data plug_num[10]; 

    void *handle;

    int (*result)(int, int);
    int i;
    char *error;

    FILE *fp;

    // 설정파일을 읽어들이고 
    // 내용을 구조체에 저장한다. 
    fp = fopen("plugin.cfg", "r");
    data_num = 0;
    while(fgets(buf, 50, fp) != NULL)
    {
        buf[strlen(buf) -1] = '\0';
        sscanf(buf, "%[^,]%[,]%[^,]%[,]%[^,]", plug_num[data_num].oper, 
                                               null,    
                                               plug_num[data_num].func, 
                                               null,
                                               plug_num[data_num].lib);
        data_num ++;
    }
    fclose(fp);

    printf("> ");
    memset(left, 0x00, 11);
    memset(right, 0x00, 11);
    fscanf(stdin, "%[0-9]%[^0-9]%[0-9]", left, oper, right);

    // 연산자를 비교해서 
    // 적당한 라이브러리를 불러온다. 
    for (i  = 0; i < data_num ; i++)
    {
        int state; 
        if ((state = strcmp(plug_num[i].oper, oper)) == 0) 
        {
            printf("my operator is      : %s\n", plug_num[i].oper);
            printf("my call function is : %s\n", plug_num[i].func);
            break;
        }
    }    

    if (i == data_num)
    {
        printf("--> unknown operator\n");
        exit(0);
    }

    handle = dlopen(plug_num[i].lib, RTLD_NOW);
    if (!handle)
    {
        printf("open error\n");
        fputs(dlerror(), stderr);
        exit(1);
    }

    // 연산자에 적당한 함수를 불러온다. 
    result = dlsym(handle, plug_num[i].func);
    if ((error = dlerror()) != NULL)
    {
        fputs(error, stderr);
        exit(1);
    }

    printf("%s %s %s = %d\n",left, oper, right, result(atoi(left), atoi(right)) ); 

    dlclose(handle);
}
				
위의 예제 프로그램은 다음과 같이 컴파일되어야 한다. 라이브러리 파일의 위치는 /usr/my/lib 아래에 있는것으로 하며, 라이브러리 찾기 경로에 등록되어 있다고 가정하겠다.
[root@localhost test]# gcc -o print_sum_dl print_sum_dl.c -ldl 
				
이 프로그램을 실행하면 사용자의 입력을 기다리는 "> "가 뜨게 되고, 여기에 계산하기 원하는 값을 입력하면 된다. 현재는 '+'와 '-' 연산만을 지원하며, 연산자와 피연산자들 간에 간격이 없어야 한다. 다음은 실행결과 화면이다.
  
[root@localhost test]# ./print_sum_dl
> 99+99
my operator is      : +
my call function is : ysum
99 + 99 = 198
[root@localhost test]#
				
사용자가 프로그램을 실행하면 프로그램은 사용자의 입력을 받아들이고 sscanf 를 이용해서 연산자와 피연산자를 구분하게 된다. 그리고 피연산자를 값으로 하여, 설정파일에 설정된 라이브러리를 불러들이고(dlopen) 해당 함수를 가져와서(dlsym) 실행시키게 된다.

자 이렇게 해서 우리는 '+', '-' 연산이 가능한 프로그램을 하나 만들게 되었다. 그런데 A 라는 개발자가 '*','/' 연산도 있으면 좋겠다고 생각해서 아래와 같은 코드를 가지는 '*', '/' 연산을 위한 라이브러리를 제작하였다.

예제 : mymulti.h

int multi(int a, int b);
int div(int a, int b);
				
예제 : mymulti.c
int multi(int a, int b)
{
    return a * b;
}

int div(int a, int b)
{
    return a / b;
}
				

A 라는 개발자는 이것을 다음과 같이 공유라이브러리 형태로 만들어서 간단한 라이브러리의 설명과 함께 email 로 전송했다.

[root@localhost test]# gcc -c -fPIC mymulti.c
[root@localhost test]# gcc -shared -W1,-soname,libmymulti.so.1 -o libmymulti.so.1.0.1 mymulti.o
				

라이브러리를 받았으므로 새로운 라이브러리가 제대로 작동을 하는지 확인을 해보도록 하자. 우선 libmymulti.so.1.0.1 을 /usr/my/lib 로 복사하도록 하자. 그다음 설정파일에 다음과 같은 내용을 가지도록 변경 시키도록 하자.

 
+,ysum,libmystuff.so
-,ydiff,libmystuff.so
*,ymulti,libmymulti.so.1.0.1
/,ydiv,libmymulti.so.1.0.1
				
이제 print_sum_dl 을 실행시켜보자.
[root@localhost test]# ./print_sum_dl
> 10*10
my operator is      : *
my call function is : ymulti
10 * 10 = 100

[root@localhost test]# ./print_sum_dl
> 10/10
my operator is      : /
my call function is : ydiv
10 / 10 = 1
				
print_sum_dl.c 의 원본파일의 아무런 수정없이 단지 설정파일만 변경시켜 줌으로써 기존의 print_sum_dl 에 "곱하기"와 "나누기"의 새로운 기능이 추가 되었다.

위에서도 말했듯이 이러한 Plug-in 비슷한 기능을 구현하기 위해서는 통일된 함수 API가 제공될수 있어야 한다.


4절. 결론

여기에 있는 내용중 동적라이브러리에 대한 내용은 솔라리스와 리눅스에서만 동일하게 사용할수 있다. Hp-Ux 혹은 윈도우에서는 사용가능하지 않는 방법이다. 이에 대한 몇가지 해법이 존재하는데, 이 내용은 나중에 시간이 되면 다루도록 하겠다. 어쨋든 솔라리스와 리눅스 상에서 코딩되고 윈도우 혹은 다른 유닉스로 포팅될 프로그램이 아니라면 위의 방법을 사용하는데 있어서 문제가 없을것이다.

 

[펌] http://www.joinc.co.kr/modules.php?name=News&file=article&sid=66#AEN22

Posted by 김용환 '김용환'

댓글을 달아 주세요

질문: 이중 포인터 사용 용도 질문(내공있음) aerowon20 / 2004-06-16 14:01
남이 짠 소스 분석 도중에 모르는 부분이 나와서여..
사실 이중포인터 사용을 해 본적이 없어서리 이렇게 고수님의 답변을 기다립니다.
이 소스의 의도와 용도는 어디 일까여?

char ** fsc_strtok(char *src, char *deli, int *nCnt)
{
char ** buff;
char * szBuff;
int nIndex;

if((szBuff = (char*)strtok(src, deli)) == NULL) {
printf("Strtok is NULL----------------------------\n");
return NULL;
}
buff = (char **)malloc(sizeof(char *));

if(buff == NULL)
return NULL;

nIndex = 0;

*buff = szBuff;

while( (szBuff = strtok(NULL, deli)) != NULL ) {
nIndex ++;
buff = (char **)realloc( buff, sizeof(char *) * ( nIndex + 1));
*(buff+nIndex) = szBuff;
}
*nCnt = nIndex;
return buff;
}
답변: re: 이중 포인터 사용 용도 질문(내공있음) leo0502 / 2004-06-16 11:02
일단 소스의 의도와 용도는 말이죠... 어떤 문장을 scan 해서 (첫번째 파라메터) 그 문장을 두번째 파라메터의 문자로 tokenize 한 뒤에 그 tokenize 된 문장의 각각의 주소를 이중 포인터로 만들어진 a...

Posted by 김용환 '김용환'

댓글을 달아 주세요