'코딩과 개발'에 해당되는 글 5건

  1. 2012.09.19 Java Launcher만들기
  2. 2009.10.30 The GNU C Library Reference Manual
  3. 2009.10.26 멀티 쓰레드 패턴
  4. 2009.10.26 Eclipse RCP 따라하기 1
  5. 2009.10.26 Java ClassLoader 이해하기

자바프로그램을 만들다 보면 정말 많은 컴포넌트를 사용하게 된다. 그런데 이러한 컴포넌트를 클래스 패스에 설정해주고 실행하는것은 여간 귀찮은 일이다. 그래서 간단하게 특정 디렉토리에 ,jar 파일들을 넣어두고 자동으로 클래스 패스가 처리된다면 편리하것이다. 이것을 위해 런처를 사용한다.

런처를 만들기 위해서는 다음과 같은 로직을 갖는다.


1. URLClassLoader 초기화

2. 리플렉션에 의한 main() 함수 호출


public static void main(String[] args) throws  Throwable  {


    URL[] jarfiles = ...

   ClassLoader   classloader 

         = new  URLClassLoader  ( jarfiles, Launcher.class.getClassLoader());

    Thread.currentThread().setContextClassLoader(cl);


    String mainclass= System.getProperty("main");

    

     /**

      * 리플렉션에 의함 원래의 메인함수 호출

      */

    Class c = Class.forName(mainclass, true,  classloader );

    Class[]  argc = { String[].class };

    Object[] argo = { args};

    

    java.lang.reflect.Method method = c.getDeclaredMethod("main", argc);

    try {

          method.invoke(null,argo);

     } catch (InvocationTargetException e) {

          throw e.getTargetException();

     }

}


URL클래스 로더와 리플렉션을 이용하면 간단하게 런처를 만들수 있다.

 

'코딩과 개발' 카테고리의 다른 글

The GNU C Library Reference Manual  (0) 2009.10.30
멀티 쓰레드 패턴  (0) 2009.10.26
Eclipse RCP 따라하기  (1) 2009.10.26
Java ClassLoader 이해하기  (0) 2009.10.26
Posted by sjokim
,

1. 안내문

2. 에러 보고

3. 메모리 할당

4. 문자 다루기

5. 문자열과 배열 유틸리티

6. 입출력 개요

7. 스트림에서의 입/출력

8. 저수준 입/출력

9. 파일 시스템 인터페이스

10. 파이프 와 FIFO

11. 소켓

12. 저수준 터미널 인터페이스

13. 수학 함수

14. 저수준 연산 함수들

15. 탐색과 정렬

16. 패턴 매칭 ( Pattern Matching )

17. 날짜와 시간

18. 확장된 문자들

19. 지역과 세계화

20. 비-지역 탈출

21. 시그널 처리

22. 프로세스의 시동과 종료

23. 프로세스

24. 작업 제어

25. 사용자와 그룹

26. 시스템 정보

27. 시스템 구성 파라미터

부록 A. 라이브러리에 있는 C 언어 기능들

부록 B. 라이브러리 기능들의 요약

부록 C. 라이브러리 유지보수

부록 D. GNU LIBRARY GENERAL PUBLIC LICENSE

  • 서론
  • 복제, 배포 와 수정에 대한 제한과 조건

 


'코딩과 개발' 카테고리의 다른 글

Java Launcher만들기  (0) 2012.09.19
멀티 쓰레드 패턴  (0) 2009.10.26
Eclipse RCP 따라하기  (1) 2009.10.26
Java ClassLoader 이해하기  (0) 2009.10.26
Posted by sjokim
,
기본 지식
synchonized, wait(), notify(), notifyAll()을 알아야 한다. 
멀티 쓰레드 패턴은 synchonized와 wait(), notify(), notifyAll()을 적절히 사용하는 방법을 제시한다.
public synchronized void put(Object o) {
queue.addLast(o);
this.notifyAll();
}
public void put(Object o) {
synchronized(this){
          queue.addLast(o);
    this.notifyAll();
      }
}
임의의 객체에 대한 wait(), notify(), notifyAll()을 호출하기 위해서는 반드시 해당 객체에 대한 lock(synchronized)을 획득해야 한다.

Guarded Suspension 패턴
쓰레드를 기다리게 하여 인스턴스의 안정성을 지킨다. Guarded wait, spin lock이라고도 불리운다.

public class GuardedQueue {
private final LinkedList queue = new LinkedList();
public GuardedQueue() {
        }

public synchronized Object get() {
while (queue.size() <= 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
return queue.removeFirst();
}

public synchronized void put(Object o) {
queue.addLast(o);
notifyAll();
}
}

자원을 제어하는 Pool에 활용할 수 있다 Ex)DB/Socket Connection, Thread Pool등등

Producer Consumer 패턴
Guarded Suspension과 다르게 버퍼를 가지고 있다.
채널의 개념을 가지고 있다.

public class Channel {
private final Object[] buffer;
private int tail;
private int head;
private int count;
public Channel(int count) {
buffer = new Object[count];
this.count = count;
}
public synchronized void put(Object cake) 
                throws InterruptedException {
while (count >= buffer.length) {
wait();
}
buffer[tail] = cake;
tail = (tail + 1) % buffer.length;
count++;
notify();
}
public synchronized Object take()  throws InterruptedException {
while (count <= 0) {
wait();
}
Object cake = buffer[head];
head = (head + 1) % buffer.length;
count--;
notify();
return cake;
}
}

Read-Write Lock 패턴 
객체에 대한 Read Lock과 Write Lock을 효과적으로 제어한다.
고비용의 리소스 제어에 활용이 가능하다.
   Read  Write
 Read  OK  충돌
 Write  충돌  충돌

public class ReadWriteLock {
private int readingReaders = 0;
private int waitingWriters = 0;
private int writingWriters = 0;
private boolean preferWriter = true; 
public synchronized void readLock() 
              throws InterruptedException {
while ( writingWriters > 0 || 
       (preferWriter && waitingWriters > 0)
     ) {
wait();
}
readingReaders++;
}
public synchronized void readUnlock() {
readingReaders--;
preferWriter = true;
notifyAll();
}
public synchronized void writeLock() throws InterruptedException {
waitingWriters++;
try {
while (readingReaders > 0 || writingWriters > 0) {
wait();
}
} finally {
waitingWriters--;
}
writingWriters++;
}
public synchronized void writeUnlock(){
writingWriters--;
preferWriter = false;
notifyAll();
}
}
abstract public class SafeAccess {
private final Object[] buffer;
private final ReadWriteLock lock = new ReadWriteLock();
public SafeAccess(int size) {
buffer = new Object[size];
}
final public Object read() throws InterruptedException {
lock.readLock();
try {
return doRead();
} finally {
lock.readUnlock();
}
}
final public void write(Object value) throws InterruptedException {
lock.writeLock();
try {
doWrite(value);
} finally {
lock.writeUnlock();
}
}
abstract protected Object doRead(); 
abstract protected void doWrite(Object value); 
}

'코딩과 개발' 카테고리의 다른 글

Java Launcher만들기  (0) 2012.09.19
The GNU C Library Reference Manual  (0) 2009.10.30
Eclipse RCP 따라하기  (1) 2009.10.26
Java ClassLoader 이해하기  (0) 2009.10.26
Posted by sjokim
,

1 개요

이클립스 RCP(Rich Client Platform) 자바로 독립 응용 프로그램을 만들기 위한 강력한 도구를 제공한다. 응용 프로그램을 만들다 보면 본래 기능보다 메뉴라든지, 도구 모음(toolbar) 처리나 프로그램 설정 관리 같은 기능들을 구현하는  오히려  많은 노력과 시간이 투자되경우가 많다. 이클립스 RCP 이러한 문제로부터 프로그래머를 해방시킨다.

지금부터 이클립스 RCP 활용해 예제 프로그램을 만드는 과정을 소개하고자 한다. 직접 따라 해보면, RCP 이용해 그럴 듯한 프로그램 하나를 개발해볼  있을 것이다. 

 

2 예제 프로그램 생성하기

이클립스 RCP 프로그램을 시작하려면 예제 프로그램에서 출발하는 것이 좋다(물론 수정하다보면 나중에는 원본 예제가 모두 사라지게 되겠지만…).

 

프로젝트 생성

다음과 같이 플러그인 프로젝트 만든다.

 

1.  Select a wizard: Plug-in Project 선택하고 Next 클릭

2.  Plug-in Project: Project Name MyRCP 입력하고 Next 클릭

3.  Plug-in Content: Rich Client Application 부분을 Yes 수정  Next 클릭

4.  Templates: RCPMail Template 선택하고 Next 클릭

5.  RCP Mail Template: 그냥 Finish 클릭

 

이렇게 하면 아래와 같이 간단한 전자메일 클라이언트가 만들어진다.

 


그림 1. 예제 프로젝트 생성 모습

 

 화면 중앙의 Launch an Eclipseapplication 부분을 클릭하면 MyRCP 실행된다.

 

3 전자메일 클라이언트를 파일 탐색기로 개조하기

이클립스 RCP 상당 부분을 설정만으로 가능하며, 프로그래밍을 하더라도 응용 프로그램의메뉴, 도구 모음 등은 최소한의 코드로   있게 되어 있다. 여기서는 프로그램적 요소를 중심으로 수정해 MyRCP 프로그램을 간단한 파일 탐색기 형태로 만들어  것이다.  이렇게 함으로써 간단한 RCP프로그램의 구조를 파악할  있을 것이다



그림 2. MyRCP 최초 실행 모습

 

3.1 전자메일 리스트 창을 파일 탐색기 수정하기

예제 프로그램에서 왼쪽 네비게이션  부분을 파일 탐색기 수정해 보자. 예제 프로그램의NavigationView.java 열어보면 jface 프로그래밍이  것을 확인할  있다. 물론 jface사용하면 편리한 점이 많으나 여기서는 swt 사용해 간단한 파일 탐색기 만들어 보겠다(jface 사용하면 좀더 풍부하게 그리고 효과적으로 응용 프로그램을 작성할  있으므로RCP프로그램을 잘하려면 jface 익혀두는 것이 좋다).

NavigationView ViewPart 자식 클래스이므로 createPartControl(Composite) setFocus() 파일 탐색기 기능을 하는 코드를 작성하면 된다(참조 NavigationView.java)

 


그림 3 NavigationView 수정하여 실행

 

레이지 로딩(lazy loading) 구현하기

NavigationView에서 보면 트리 컴포넌트를 구현할  간단한 팁이 들어 있다. 파일 리스트를트리 형태로 보여   목록을 모두 읽을 필요는 없다. 성능 저하를 막기 위해 참조가 발생했을  자식 정보를 읽어 들이도록 코딩된 모습을 확인할  있다.

 

private void buildChildItem(TreeItem item,File file, boolean isroot) {

       if(isroot)

               item.setText(file.getAbsolutePath());// 아이템 루트이면 절대 경로 라벨로 사용

       else

               item.setText(file.getName());// 아이템 루트 아니면 이름만 라벨로 사용

        if(file.isFile()) {

                           item.setText(1,formatSize(file.length()));

                           item.setText(2,formatTime(file.lastModified()));

         }

             item.setImage(getImage(file));

             item.setData("path",file);

             if (hasChild(file)) {

                           item.setExpanded(false);

                           newTreeItem(item, 0); // 더미 아이템

             }

}

 

트리에서 특정 노드의 자식을 보여   해당 아이템에 자식이 존재하는지만을 확인한  만약 자식이 존재한다면 더미 아이템을 추가해 놓고 펼침(expand) 상태를 false  놓는다. 그렇게 하면 해당 트리 아이템 자식이 존재한다는 [+] 표시 보이게 된다. Expand 이벤트가발생할   아이템이 더미 아이템을 삭제하고 실제 자식 아이템들을 보여준다.

다음으로 해당 아이템 펼쳐질  이전에 방문했는지를 검사하여 방문한 적이 없는 아이템 대해서만 방문 기록을 세팅을 하고 buildChild 통해 자식 트리 초기화한다.

 

fileTree.addListener(SWT.Expand, newListener() {

             publicvoid handleEvent(final Event event) {

                           finalTreeItem item = (TreeItem) event.item;

                           if (item.getData("visited") ==null) { // 이전에 방문했는지 확인

                                        item.setData("visited","true"); // 해당 아이템에 방문했음을 설정

                                        buildChild(item);

                           }

             }

};

 

3.2 메시지 창을 파일 뷰어 수정하기

예제에서 오른쪽 메시지 창은 View.java 구현되어 있다. 이것을 매개변수 파일 이름을 받아 화면에 파일 내용을 보여주는 파일 뷰어 수정한다(참조 View.java)
. 
단지 선택된 파일을 읽어 들이는 것이기 때문에 기술적으로 복잡할 것은 없지만 다만 열리는 창에 파일 이름 전달하는 방법이 간단한 이라   있다.

 

MyRCP 프로그램의 텝윈도우 Open Logic

기본적으로   독립적인 컴포넌트다. 따라서 다른 창에서 명시적으로 정보를 전달하기보다는 이벤트 모델을 통해 창들간에 데이터를 전송하는 것이 좋은 방법이나 여기서는 Secondary ID 파일 이름을 전달하는 약간의 트릭을 이용해 전달하고 있다.

 

public static HashMap selection = new HashMap();

public static void openView(String viewId,String path) {

             if(path == null)

                           return;

             IWorkbenchWindowwindow = MyRCPPlugin.getDefault().getWorkbench()

                                        .getActiveWorkbenchWindow();

             selection.put("" +path.hashCode(), path);

             try{

                           window.getActivePage().showView(viewId,"" + path.hashCode(),

                                                     IWorkbenchPage.VIEW_ACTIVATE);

             }catch (PartInitException e) {

                           MessageDialog.openError(window.getShell(),"Error",

                                                     "Erroropening :" + e.getMessage());

             }

}

 

Secondary ID에는 특수 문자를 사용할  없기 때문에, HashMap 파일 이름의 해시 저장하고 에서 해시 코드 사용해 넘겨진 파일 이름 알아내는 방식을 취하고 있다.  방식을 이용할 경우selection(HashMap) element 수가 계속 늘어날 여지가 있다.

다음 코드는  createPartControl()에서 전달되는 Secondary ID 이용해 파일 이름을 알아내고 화면에 출력하는 것이다.

 

public void createPartControl(Compositeparent) {

             Stringhash = this.getViewSite().getSecondaryId();

             Stringpath = (String)Perspective.selection.get(hash);

             if(path == null)

                           return;

 

3.3 시작 메뉴 수정  초기화

이제 메뉴를 수정한다. 최초의 전자메일 프로그램에서 사용하던 불필요한 메뉴를 제거하고시작할  필요 없는  열리지 않도록 로직을 수정한다.

 

메뉴 구조 수정

메뉴와 도구 모음 관한 프로그램 코드(ApplicationActionAdvisor.java) 보면, 매우 직관적으로 구현되어 있기 때문에 고치는  어려움이 없을 것이다. 만약 메뉴나 도구 모음 아이템을 추가하고자 한다면 원본 코드에 존재하는 로직을 참고해 동일한 형태로 프로그램을 짜면된다. fillMenuBar(MenuBar 초기화)에서 exitAction aboutAction만을 남기고 나머지는 모두 주석 처리한다. 그리고 fillCoolBar(CoolBar 초기화) 수정해 exitAction aboutAction실행할  있도록 변경한다.

 

화면 크기 수정

예제 프로그램을 보면 화면 영역이   있는데 왼쪽은 25%, 오른쪽은 75% 차지하고 있다. 프로그램이 시작할  이렇게 열리는 화면 영역의 크기를 조정하기 위해서는Perspective.java에서 createInitialLayout(IPageLayout layout) 수정한다.

layout.addStandaloneView(NavigationView.ID,false, IPageLayout.LEFT, 0.25f,editorArea);layout.addStandaloneView(NavigationView.ID, false, IPageLayout.LEFT, 0.5f, editorArea);수정하면 프로그램이 시작될   개의 화면 영역이 각각 50% 점유하게 된다. 그리고folder.addView(View.ID); 주석 처리하면 시작할  불필요한 메시지 화면이 열리지 않는다.

 


그림 4 화면 영역 비율을 수정하여 시작한 모습

 

4 MyRCP 익스포트하기

이제 MyRCP 독립 프로그램으로 실행하기 위해 익스포트한다.

4.1 제품 만들기

만들어진 프로그램을 설정을 통해 익스포트하면 그것이 독립 응용 프로그램이 된다.

 

제품 설정 파일 생성

프로젝트에서 마우스 오른쪽 버튼을 클릭해 New->Product Configuration 선택한다.

 


그림 5 제품 Configuration 생성

 

‘New Product Configuration’ 화면에서 MyRCP 선택하고, ‘File name’ MyRCP.product 입력하고 Finish 클릭한다. 그러면 독립 프로그램으로 실행하기 위한 각종 설정 값들을 지정할  있는 화면이 열린다.

생성된 MyRCP.product 설정은  개의 으로 구성되어 있는데, 간편하게 OverviewConfiguration  개의  설정하면 익스포트  있다.

 

오버뷰 설정

Overview  화면에서 프로그램 이름 같은 기본 정보를 설정한다.

 


그림 6 오버뷰 설정

 

‘Product ID’ 오른편 New 버튼을 클릭하면 ‘New Product Definition’ 화면이 나타난다.


그림 7 제품 정의

 

먼저 ‘Defining Plug-in’ 오른편의 Browse 클릭하여 ‘Plug-in Selection’에서 MyRCP 선택하고 OK 누른다. 둘째로 ‘Product ID’ 임의의 값을 입력한다. 예제에서는 MyRCP 입력했다. 그리고 Finish 선택하면 아래와 같이  필드에 입력 값들이 등록되는 것을   있다.

 


그림 8 오버뷰 입력된 모습

 

설정

MyRCP.product에서 Configuration  선택하면 아래와 같이 비어있는 화면을   있다. MyRCP 독립 응용 프로그램으로 실행하기 위해서는 필요한 플러그인들이 전부 같이 익스포트되어야 한다.

 


그림 9 설정

 

Add 버튼을 누르면 플러그인 선택하는 화면이 보인다.   MyRCP(제품 포함되어야 하는 기본 플러그인) 선택한다.

 



그림 10 MyRCP 플러그인 추가

 

MyRCP 플러그인 동작하려면 의존 관계에 있는 다른 플러그인 모두 추가돼야 하지만 이클립스에는  작업 자동으로 수행해 주는 기능이 있다. 화면에서 ‘Add RequiredPlug-ins’클릭하면 이미 추가된 MyRCP 동작하기 위해 필요한 모든 플러그인들이 자동으로 추가된다.

 



그림 11 필요한 플러그인 자동 추가

 

제품 익스포트  실행

MyRCP 독립 응용 프로그램으로 수행하기 위해 익스포트 해야 한다.

 



그림 12 익스포트

 

MyRCP.product Overview 화면에서 Exporting 영역의 ‘Eclipse Product export wizard’ 클릭해 익스포트 수행한다.

 



그림 13 익스포트 위치 지정

 

Export 창에서 ‘Archive file’ 적절한 경로 입력하고 Finish 클릭하면 익스포트 수행된다. 익스포트 파일을 적당한 디렉터리에 풀어서 실행하면 된다.

 

4.2  기타 유용한   가지

사소하지만 처음 RCP 만들  공유하고자 하는 내용을 정리해 보았다.

 

설정 사용하기

이클립스에서는 환경 설정을 위한 기능을 제공하고 대부분의 설정 정보는 이곳에 넣는 것이일반적이다. 그러나 java –Dxxx=100 같이 사용하고자 한다면 configure/config.ini 값을 넣고 System.getProperty(“xxx”)형태로 프로그램에서 읽어 사용할  있다.

 

메모리 지정하기

MyRCP 자바 프로그램이기 때문에 메모리 지정이 필요하다 익스포트 전에MyRCP.product Configuration 에서 지정할 수도 있지만, 메모리 옵션을 바꾸려고 매번 익스포트 수는 없다.

메모리를 포함한 JVM 옵션은 eclipse.ini 저장되어 있다. 그런데 만약 Configuration 에서JVM 옵션을 지정하지 않으면 eclipse.ini 파일 자체가 만들어지지 않는다.

 

 모양 바꾸기

실행한 화면을 보면  이클립스와 모양이 다른 것을   있다. 이것을 수정하려면plugin_customization.ini 파일에 속성 설정이 필요하다. configuration/config.ini 파일에 필요한설정을 넣고 그것을 프로그램에서 사용하면 효과적이다. config.iniorg.eclipse.ui/SHOW_TRADITIONAL_STYLE_TABS=false 설정되면  모양이 이클립스처럼 부드럽게 변경된다. 속성을 적용하려면 plugin_customization.ini MyRCP 플러그인같이 패키징되어야 한다.

 



그림 14 MyRCP 프로젝트 구조

 



그림 15 빌드 목록에 plugin_customization.ini 추가

 

Build.properties 파일을 열어 위와 같이 바이너리 빌드에서 체크 주면 같이 패키징된다.



그림 16  모양이 바뀐 MyRCP

 

다시 MyRCP 실행하면 바뀐 모양을 확인할  있다.

 

결론

RCP초보자라면 지금까지 간단한 샘플을 통해서 RCP 대한 몇가지 의미있는 가치들을 발견할  있었을 것이다. 응용프로그램을 자바로 하면서도 4GL보다 나은 프로덕을 쉽게 만들수있다는 것은 정말 강력한 매력이다.

아직은 RCP 시장에서 붐은 아니지만 이클립스 RCP 접한 사람중에서 그가치를 높게 평가하지 않는 사람은 없다. 자바 개발자라면 RCP 접해두는 것은  필요하고도 가치있는 일이다.


'코딩과 개발' 카테고리의 다른 글

Java Launcher만들기  (0) 2012.09.19
The GNU C Library Reference Manual  (0) 2009.10.30
멀티 쓰레드 패턴  (0) 2009.10.26
Java ClassLoader 이해하기  (0) 2009.10.26
Posted by sjokim
,

1 java에서 class path

자바는 클래스를 기반으로 동작하도록 만들어진 프로그램 언어이다. 그리고 자바 클래스를 실행하는 엔진을 자바 가상 머신(JVM)이라 한다. JVM은 물리적으로 java.exe라는 실행 파일과 부속 라이브러리로 되어있다.

JVM(java.exe)이 수행할  클래스의 위치가 classpath이다.

클래스패스 설정

Java.exe가 인식할 수 있는 클래스패스에는 여러 가지가 있다. 그중에서 크게 java.exe가 실행하면서 로딩하는 bootclass java.lang.ClassLoader에 의해 로딩되는 일반 클래스가 있다.

Bootclass java.exe에 의해 로딩되면 JVM이 초기화되면서 필요한 클래스들로 이루어져 있다.System.getProperty("sun.boot.class.path")에서 확인할 수 있다.

JVM초기화 과정이 끝나면 java java.lang.ClassLoader를 통해서 어플리케이션 클래스들을 로딩한다. System.getProperty("java.class.path")에서 값을 확인할 수 있다.

Hello 클래스를 만들어 실행하는 방식을 알아보자.

>Hello.java를 컴파일하여 hello.jar파일 생성

1.       Jar파일을 CLASSPATH환경변수에 설정

>set CLASSPATH=hello.jar

>java test.Hello

2. jar파일을 -cp 혹은 -classpath를 이용하여 직접 설정

>java  -cp hello.jar  test.Hello

>java  -classpath hello.jar test.Hello

3. Xbootclasspath hello.jar를 설정하여 실행하는 방법

>java  -Xbootclasspath/p:hello.jar  test.Hello

>java  -Xbootclasspath/a:hello.jar  test.Hello

4. JAVA_HOME/jre/lib/ext hello.jar를 복사하고 실행

>copy hello.jar %JAVA_HOME%/jre/lib/ext/.

>java  -Xbootclasspath/a:hello.jar  test.Hello

JAVA_HOME/jre/lib/ext의 파일들은 System.getProperty("java.class.path")에 표현되지 않는다.

5. -jar를 이용하여 바로 실행

Jar파일내에 /META-INF/MANIFEST.MF 파일을 만들어 넣는다. 만약 존재하면 다음과 같이Main-Class: 속성을 추가한다.

Manifest-Version: 1.0

Main-Class: test.Hello

그리고 다음과 같이 실행하면 test.Hello가 실행된다.

>java  -jar hello.jar

 -jar로 수행할 때 클래스 패스를 추가하려면

Manifest-Version: 1.0

Main-Class: test.Hello

Class-Path: a.jar b.jar

클래스 로딩 순서

만약 동일한 클래스가 여러곳에 설정되어있을 때 로딩되는 우선순위는 다음과 같다.

>Xbootclasspath에 설정된 파일  ç BOOT LOADER

> JAVA_HOME/jre/lib/ext에 있는 파일  ç EXT LOADER

>MENIFEST.MF Class-Path:에 설정된 클래스  ç APP LOADER

>-cp or -classpath에 설정된 파일                   ç APP LOADER

>환경변수 CLASSPATH에 설정된 파일               ç APP LOADER

ü        Java  -jar로 클래스를 실행한 경우 클래스패스를 추가하고자 하면JAVA_HOME/jre/lib/ext에 복사하거나 MENIFEST.MF Class-Path를 편집하는 방법이 있다.

ü        App Loader를 위한 클래스 패스 설정은 같이 사용할 수 없고 그 중에서 하나를 선택해야 한다.

클래스 로더 구조

자바에서 모든 클래스로더는 java.lang.ClassLoader child클래스 이다. 클래스 로더에서 말하는Parent/Child관계는 상속관계를 의미하는 것이 아니고 ClassLoader Chain에서 선행 Node를 의미한다.


여기서 ExtClassLoader AppClassLoader Parent ClassLoader이다. 마찮가지로AppClassLoader SubAppClassLoader parent이다.

ClassLoader의 주요 메소드의 소스를 파악하면 쉽게 이해 할 수 있다.

protected Class findClass(String name) throws ClassNotFoundException {

             throw new ClassNotFoundException(name);  ç 각 로더 마다 재정의된다.

 }

 

protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException

    {

             // First, check if the class has already been loaded

             Class c = findLoadedClass(name);

             if (c == null) {

                 try {

                           if (parent != null) {

                               c = parent.loadClass(name, false); ç ParentLoader 에게 먼저 요청

                           } else {

                               c = findBootstrapClass0(name);

                           }

                 } catch (ClassNotFoundException e) {

                     // If still not found, then call findClass in order to find the class.

                     c = findClass(name);

                 }

             }

             return c;

    }

각 클래스로더는 findClass를 재정의해야 한다.각 로더의 findClass  클래스 패스에서  요청된 클래스가 존재하는지 확인하고 로딩한다.

개별 로더는 자신의 클래스 패스를 검색하기 전에 parent클래스 로더에게 해당 클래스 로딩을 먼저 요청한다. 따라서 자바에서 동일한 파일을 여러 곳에 복사하더라도 항상 최상위 클래스패스의 클래스가 로딩된다.

2 Application Server ClassLoader

단순한 자바 Application의 클래스로더는 SystemClassLoader(BootClassLoader), ExtClassLoader, AppClassLoader 3계층으로 구성된다. 그러나 WAS Servlet컨테이너와 그 아래에 각 Servlet Context를 위한 클래스 로더를 별도로 두고 있다.

 



AppClassLoader ServletContainerClassLoader는 같이 사용되기도 하지만 보통 분리된다. Tomcat의 경우 ServletContainerClassLoader TOMCAT_HOME/common/lib의 클래스들을 로딩한다.

ServletContainerClassLoader ServletContextClassLoader WAS벤더에 의해 구현되기 때문에 세부적인 상황이 버전에 따라 다를 수 있다. 따라서 구체적으로 클래스를 어떻게 배치할 것인가는 해당 WAS의 구현을 확인하고 결정해야 한다.

3 Servlet 2.3 spec for ClassLoader

서블릿 스팩을 보면 WAR를 위한 클래스 로딩 방식이 일반 자바 클래스 로더와 규칙이 다름을 알 수있다.

Servlet Spec

SRV.9.5 Directory Structure

The web application classloader must load classes from the WEB-INF/ classes directory first, and then from library JARs in the WEB-INF/lib directory.

..

SRV.9.7.2 Web Application Classloader

The classloader that a container uses to load a servlet in a WAR must allow the developer to load any resources contained in library JARs within the WAR following normal J2SE semantics using getResource().

It must not allow the WAR to override J2SE or Java servlet API classes.

It is further recommended that the loader not allow servlets in the WAR access to the web container’s implementation classes.

It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs.

스팩과 클래스 로더

-         WEB-INF/classes WEB-INF/lib는 동일 로더에 의해 로딩되지만 로더는 WEB-INF/clasess를 먼저 검색/로딩한다(must)

-         Servlet-API를 구현한 서블릿 컨테이너 클래스를 웹 어플리케이션이 Access할 수 없도록 하라(recommend)

-         WAR내부의 클래스를 컨테이너 클래스 패스의 클래스보다 먼저 로딩하라(recommend)

서블릿 스팩의 일부 모호한 점으로 인해 (recommend)형태의 항목은 서브릿 엔진에 따라서 다르게 구현하고 있거나 옵션 처리되어 있을 것이다.

4 ServletContextClassLoader구현

서블릿 스팩을 근거로 하여 ServletContextClassLoader(이하 SCCL)의 구현하기 위해서는 다음과 같은 형태로 로직이 구현되어야 한다. 클래스 load 요청이 들어 오면 먼저 자기 자신의 클래스 패스를 검색하고 이후에 parent 로더에게 요청한다.

 protected synchronized Class loadClass(String name, boolean resolve) {

             // First, check if the class has already been loaded

             Class c = findLoadedClass(name);

           If(c == null){

try{

c = findClass(name); ç Own Classpath 먼저 검색

} catch (ClassNotFoundException e) {

if (parent != null) {

                     c = parent.loadClass(name, false);

                  } else {

                     c = findBootstrapClass0(name);

                  }

}

}

             return c;

    }

 

위 로직은 서블릿 스팩을 완전하게 구현한 것이 아니다. 실제 서블릿 스팩을 보면 servlet API 관련한 클래스는 WAR가 아닌 Servlet Container에서 로딩하도록 되어있다. 따라서c=findClass(name)을 수행하기 전에 로딩해야 할 클래스가 서블릿 API관련한 클래스인지를 검사하는 로직이 필요하다. 만약 로딩해야할 클래스가 서블릿API관련 클래스이면 parent.loadClass()를 먼저 호출하도록 구현되어야 한다.

5 WAR 개념과 클래스 로딩의 우선순위

근원적으로 컴포넌트 컴포넌트 내부의 정보를 감추고 인터페이스 만으로 외부와 통신한다. WAR는 이러한 컴포넌트 개념을 지원하고 있으며 따라서 동일한 클래스가 WAR 외부와 내부에 존재할 때WAR는 내부의 클래스를 우선적으로 사용해야 한다


 

ContainerClassLoader WARClassLoader Parent 클래스이다. 따라서 Java클래스 로더 정책에 의해서는 Container ClassA가 로딩되어야 한다. 하지만 Servlet Spec2.3에 의해서 WAR Class A 가 로딩된다. 컴포넌트 개념에서는 외부 클래스가 아닌 내부 클래스 (Class A)가 사용되어야만 내부 로직이 바뀌어도 외부에 영향을 미치지 않으며, 컴포넌트(WAR)가 어느 컨테이너에 설치되더라도 내부 로직이 일관되게 동작할 것이다.

그러나 이것은 공통 클래스의 관리를 어렵게 할 가능성이 높다. 다음 예를 보자.

 



OracleConnection은 컴포넌트에 의존적이라기 보다는 외부자원(DBMS)에 의존적인 클래스 이다. JDBC드라이버가 WAR파일 안에 존재 한다면, JDBC 드라이버의 버전을 관리하는데 있어 모든WAR파일을 재배포해야 하는 상황이 발생하게 된다. 또한 위와 같이 Container의 공통클래스가DB를 사용하기 위해서 OracleConnection을 사용해야 하는 상황이 발생하고 이 클래스의 내용이WAR파일의 클래스와 참조관계가 형성되는 경우에는 VerifyError혹는 ClassCastException등이 발생할 수 있다.

 

따라서 WAR파일에 JDBC드라이버와 같이 외부 의존적인 클래스 혹은 Framework과 같은 공통 클래스를 같이 두는 것은 좋은 정책이 아니다. 내부 로직만을 위한 클래스이면서 외부요인에 의해서 변경되지 않아도 되는 라이브러리 클래스(ex. Regexp, Parser) WAR파일 내에 두는 것이 좋다.


'코딩과 개발' 카테고리의 다른 글

Java Launcher만들기  (0) 2012.09.19
The GNU C Library Reference Manual  (0) 2009.10.30
멀티 쓰레드 패턴  (0) 2009.10.26
Eclipse RCP 따라하기  (1) 2009.10.26
Posted by sjokim
,