애초 발단은..xml책을 보다가.. 


3tier를 구분하기 위한 디자인으로.. dbhandler, htmlwrapper 클래스를 만들어 서블릿의
역할을 래핑해서 분리하는 작업중.. 서블릿,디비핸들러,htmp래퍼간 어떤 방식으로 통신
하느냐에 대해 Env 클래스(HashTable파생해서 만든클래스)를 사용하겠다는 목적으로..(유연성)

디비핸들러와 html랩퍼를 만들어 두면.. sql쿼리와 html페이지만 만들어두면 쉽게 3tier app를
만들수 있다는 설명과 함께 구현을 보여주는데..(xml or 기타 파일을 이용한 스크립트 처리)


단락중에.. 환경설정을 저장하고 로딩할때 Env vs Properties 부분에서..

1.property가 문자열/문자열 이기에.. 문자열/문자배열까지 지원을 못한다는 문제점..
2.db핸들러 환경설정은 여러줄을 실행하는 sql쿼리들인 경우가 많고.. 프로퍼티의 load메소
드는 이들을 다 수용할 유연성이 없다고 하며.. (이게 오래전책이라 그런가? 1.5에서 도입된 loadFromXML 있는데..)
3.최종적으로 임의의 레벨을 갖는 환경 변수를 설정해야 하기 때문(Env가 subEnv를 가지고 subEnv가 다시 subEnv를 갖고..
트리형식)

이 말중.. 각 Env는 내부적으로 데이터를 교류하는 여러 JavaBeans들중 하나를 나타낸다. 사실 빈즈들은
끼워넣은 Env들의 구조로 변환된 XML 도큐먼트라고 할수 있고 , XML과 마찬가지로 요소들을 임의의 레벨에
넣을수 있다. 이것은 Env클래스도 마찬가지이다.

딱 요 단락을 보고.. 스윙의 컴포넌트들은 모두가 beans규약 이다는 글귀 + 내가 작업할때 컴포넌트들에 다들..
hashset형태의 테이블이 존재했었다는것.. propertyChangeSuporter를 써본 경험등.. 

그런 기억땜에 스윙 컴포넌트들의 베이스 클래스인.. jcomponent를  분석할 필요성이 느껴졌고.. 조금 살펴보다 .. 
이런저런 위임 객체들및 .. addNotify()를 보게 되었는데.. 예전에 제대로 이해 못하고 넘어간 Object클래스의
notify메서드및 옵저버 패턴이 떠올라.. 조금 살펴보던중.. 정신이 안드로메다로 떠나버렸음.. 

(아참.. Object클래스에서 notify와 wait가 있는데.. 
서로 다른 스레드간 공유되는 락객체를 만들고.. 이 락객체를 얻고, 놓아주고 하는 방법을 통해 스레드간 통신(동기화)을
하게하는 개념이다.
Object lock객체 = new Object();  syncronized(lock){lock.wait()를 해버리면.. 현재의 스레드가 대기+락객체lock를
놓아주어 다른 스레드가 락객체를 취득할수 있게 한다.}

다른 스레드에 의해 특정 이벤트 발생시.. syncronized(lock){lock.notify()식으로 하면.. 저 위에 멈췄던 스레드 다시
가동(throw InterruputException을 통해서 = 예외 핸들링 방식.. 물론 중간에 새로운 스레드를 생성및 동작시키는 등의 
처리도 가능하고.. 내 맘대로지..} => 결국 스레드간 이벤트 방식으로 동작시킬수 있음.

예전 단순 멀티스레드 서버 구현시 어셉,리드 작업의 비동기 처리를 위해 어셉 스레드에서 어셉후, 리드 스레드를
생성해서 동작시키는 방식이었지만.. wait,notify를 이용한다면.. 서버 처음 시작부터.. 어셉스레드 동작, 리드 스레드
동작시키고.. 지들끼리 내부적으로 wait/notify로서 생산자/소비자 방식으로 순차적 처리가 가능함. 

물론 위의과정은 스레드가 각각 하나씩인 단순한 경우로서.. 더 복잡해질 경우.. 어셉 스레드풀 + 어셉용 스레드 3개
리드용 스레드풀 + 리드용 스레드 10개라면.. 어셉 스레드들과 리드 스레드들간의 생산/소비자 방식의 순차적 처리+
어셉스레드는 자기들끼리, 리드스레드는 자기들끼리 동기화를 시켜줘야함.(지들끼린 코드를 공유하니.. 메서드의
선언에 synchronized를 한다던가 하는 식 또는 공유 데이터를 동기화 객체(콘커런시인가 하는 패키지)으로.. 동기화 처리)

실제 구현은 안해봄;;  결국.. 작업 시작해라=notify, 작업 잠시 멈춰라=wait 라는 .. 이벤트 객체인 셈임..
이벤트 처리 방식은 .. 전에 잠깐 살펴본 생산자/소비자 패턴임. = 예전 nio할때.. 어셉용 스레드가 큐=fifo에 클라이언트 
메세지들을 넣어두고.. 큐객체가 발송 작업용 스레드1놈만 호출해서 작업시키는 방식이랄까? 물론.. 큐의 원소 하나당
스레드 하나씩이지.. 전체를 스레드 하나가 처리하는건 아님;;)
*****************************************************************8

여튼 xml등 파일을 통한.. 설정의 저장/로드를 통한 초기화는 다른 프레임워크.. 스프링/스트럿츠등에서도 사용하는 개념
이기에 뚤어둘 필요가 있음;; ..초 간단하게 개념잡자면.. xml파일에 readObject/writeObject 함으로서 확장성 높이고,
결합성 줄이고..문자열이라 사람이 이해하기 쉽고..
(현재 xml책을 보다->grep코드를 보다->스윙책중 document부분을 보다.. -> 인터넷에서 jcomponent를 찾고있는중;;;)


결론: 자바 api클래스들중 특히 gui클래스들.. 정말 더럽게 복잡하고.. 거대하고.. 사람 환장하게 함;;

jcompent가 임포트하는 클래스만 해도.. OTL


 import java.util.HashSet;
29  import java.util.Hashtable;
30  import java.util.Dictionary;
31  import java.util.Enumeration;
32  import java.util.Locale;
33  import java.util.Vector;
34  import java.util.EventListener;
35  import java.util.Set;
36  import java.util.Map;
37  import java.util.HashMap;
38  
39  import java.awt.*;
40  import java.awt.event.*;
41  import java.awt.image.VolatileImage;
42  import java.awt.Graphics2D;
43  import java.awt.peer.LightweightPeer;
44  import java.awt.dnd.DropTarget;
45  import java.awt.font.FontRenderContext;
46  import java.beans.PropertyChangeListener;
47  import java.beans.VetoableChangeListener;
48  import java.beans.VetoableChangeSupport;
49  import java.beans.Transient;
50  
51  import java.applet.Applet;
52  
53  import java.io.Serializable;
54  import java.io.ObjectOutputStream;
55  import java.io.ObjectInputStream;
56  import java.io.IOException;
57  import java.io.ObjectInputValidation;
58  import java.io.InvalidObjectException;
59  
60  import javax.swing.border.*;
61  import javax.swing.event.*;
62  import javax.swing.plaf.*;
63  import static javax.swing.ClientPropertyKey.*;
64  import javax.accessibility.*;
65  
66  import sun.swing.SwingUtilities2;
67  import sun.swing.UIClientPropertyKey;


by givingsheart 2014. 1. 2. 09:41


Thread workHard = new Thread()
{ //익명 클래스 기법
  public void run()
  {
        doToughWork(); //이벤트처리및 페인팅 작업등.. 우선 처리 해야할 작업들 (1스레드 임으로 blocking)

        SwingUtilities.invokeLater(new Runnable()
        {
            //메서드의 매개변수로 객체를 생성해서 넘길때도 익명클래스 기법으로 재정의 가능
            public void run()
            {
                updateConponents(); //컴포넌트의 프로퍼티등 상태 수정.. 등의.. 가벼운 작업..
             }
         };
   }
};

workHard.start(); 

=>음..뭐랄까.. 일단 스레드간 병목현상을 없애기 위해서.. 가장 시간이 많이 소요되는 작업을 우선 처리한 후
   그 작업 스레드에서 새로운 스레드를 생성해 다음 작업을 처리시킴... 
   =>서버 소켓의 accept() 후  클라에 대한 i/o용 작업 스레드를 생성해서 start() 시킨것과 같음.

음.. 멀티스레드에서 병목현상이 있다면.. 전에 남궁성선생님 책에서 yield()인가를 쓰면 된다고 했었는데...
아.. yield()는 병목현상이 아니라.. 유휴시간이었던가... 글고 또 다른 방법이 생각났었는데.. 그새 까묵....OTL

하여튼.. 스레드간 순차적인 처리를 하게 한다.

*************************************************************8

음.. 사용자가 스레드 작업을 멈출수 있게.. doToughWork();를 구현

public void doToughWork()
{
  try
  {
      //작업 종료시(모든 객체를 반복해가며 while(){for(객체갯수){doUpdat(); doPaint();}})로 while의 종료조건
      //을 구성하지 말고!  while 업무 종료조건을.. 예전에 nio 헀을당시 스레드 처리 방식처럼..
      
      while(true) //예전 nio땐 클라측 스트림 객체가 not null인 동안
        for(처리할 객체의 갯수만큼)
        {
           if(Thread.interrupted() ) //현재 스레드가 인터럽트 상태인가? 
           {
              throw new IntterruptedException(); <-- 익셉션 발생을 통한 반복문 제어 처리
           }
            객체.update();
            객체.draw();
         }
       Thread.wait(1000); //현재 스레드가 한타임 뛰었으니 1초 휴식 -_-;
  }
  catch(IntterruptedException e)
  {
      //스레드 인터럽트를 알리고
      //객체들 상태가 변화했으면 
      // invokerLater를 호출한다.
   }
}

사용자용 Jbutton의 리스너에

actionPerform(Event e) 인가 에서
{
   workHard.interrrupt(); 를 해주면..
}


(추가)

스윙..컴포넌트 기반의 GUI.. 추후 자바스크립트중 프론트 영역의 MVC처리 , 안드로이드 에서.. 조립및 재사용 단위에

있어서 도움이 되었다.

'자바se api > swing' 카테고리의 다른 글

스윙..  (0) 2014.01.02
이벤트처리 스레드;;  (0) 2014.01.02
propertyEditor vs ConversionService + 토비의 스프링?  (0) 2014.01.02
자바 스윙 겁나 복잡하고 방대함...  (0) 2014.01.02
by givingsheart 2014. 1. 2. 09:28
스윙책에서 이벤트처리 스레드를 강조했기에 (java.awt.EventDispatchThread)

이번 기회에 분석하자 해서 봤더만... 머리아픔..

63 class More ...EventDispatchThread extends Thread {
64 
65     private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventDispatchThread");
66 
67     private EventQueue theQueue; // 이벤트큐 구현 코드는 제일 아래쪽에 (자기=스레드가 처리할 작업거리를 필드로
                                                         갖고있다..
68     private boolean doDispatch = true;
69     private boolean threadDeathCaught = false;
70 
71     private static final int ANY_EVENT = -1;
72 
73     private Vector<EventFilter> eventFilters = new Vector<EventFilter>();//체인오브 리스폰스빌리티처럼,.. 필터개념
//으로 이벤트 검사
74 
75     More ...EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
76         super(group, name);
77         setEventQueue(queue); //aggregation.. 내 경운 전에 worker와 work를 association으로 해서 worker.setWork(work)
78     }
79 
80     /*
81      * Must be called on EDT only, that's why no synchronization
82      */
83     public void More ...stopDispatching() {
84         doDispatch = false;
85     }
86 
//아래가 기본 동작 run()
87     public void More ...run() {
88         while (true) {
89             try {
90                 pumpEvents(new Conditional() {
91                     public boolean More ...evaluate() {
92                         return true;
93                     }
94                 });
95             } finally { //리턴을 해도 finally는 무조건 실행됨.(내가 전에 이것땜에 당황했었음)
96                 EventQueue eq = getEventQueue();
//아래는 run의 while(true) 종료 조건임 이게 finally구문 안에 있는것을 이해할것!
97                 if (eq.detachDispatchThread(this) || threadDeathCaught) {
98                     break;
99                 }
100            }
101        }
102    }
103
104    void More ...pumpEvents(Conditional cond) {
105        pumpEvents(ANY_EVENT, cond);
106    }
107
108    void More ...pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
109        pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
110    }
111
112    void More ...pumpEvents(int id, Conditional cond) {
113        pumpEventsForHierarchy(id, cond, null);
114    }
115
116    void More ...pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) {
117        pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
118    }
119
120    void More ...pumpEventsForFilter(Conditional cond, EventFilter filter) {
121        pumpEventsForFilter(ANY_EVENT, cond, filter);
122    }



166    boolean More ...pumpOneEventForFilters(int id) {
167        AWTEvent event = null;
168        boolean eventOK = false;
169        try {
170            EventQueue eq = null;
171            EventQueueDelegate.Delegate delegate = null;
172            do {
173                // EventQueue may change during the dispatching
174                eq = getEventQueue();

//아래는 getDelegate() 메서드에서 뭘하는지 확인 필요..(AppContext는 프로그램을 의미하는 전역
//객체라 생각하는게 편할듯)
//프로그램별로 이벤트 처리 담당하는 녀석들이 set에 들어있나봄.. swing의 컴포넌트들도 그렇던데..
39    public static Delegate More ...getDelegate() {
40        return
41          (Delegate) AppContext.getAppContext().get(EVENT_QUEUE_DELEGATE_KEY);
42    }
175                delegate = EventQueueDelegate.getDelegate();
176
//이벤트 처리할 delegate를 얻었으니.. 큐에서 이벤트 꺼내오기.
177                if (delegate != null && id == ANY_EVENT) {
178                    event = delegate.getNextEvent(eq);
179                } else {
180                    event = (id == ANY_EVENT) ? eq.getNextEvent() : eq.getNextEvent(id);
181                }
182
//아래 로직을 보니.. eventOK = false; 코드가 눈에 띄는데.. 여러 필터단에서 해당 이벤트가 
//처리할 이벤트인지를 검증하는듯..
183                eventOK = true;
184                synchronized (eventFilters) {
185                    for (int i = eventFilters.size() - 1; i >= 0; i--) {
186                        EventFilter f = eventFilters.get(i);
187                        EventFilter.FilterAction accept = f.acceptEvent(event);
188                        if (accept == EventFilter.FilterAction.REJECT) {
189                            eventOK = false;
190                            break;
191                        } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
192                            break;
193                        }
194                    }
195                }
//필터로도 부족해서 다른 메서드를 통해 이벤트인지를 확인
196                eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
197                if (!eventOK) {
198                    event.consume(); //말그대로.. 이벤트를 처리한달까? 소모시키기.. 아래처럼 구현
418    protected void More ...consume() {
419        switch(id) {
420          case KeyEvent.KEY_PRESSED:
421          case KeyEvent.KEY_RELEASED:
422          case MouseEvent.MOUSE_PRESSED:
423          case MouseEvent.MOUSE_RELEASED:
424          case MouseEvent.MOUSE_MOVED:
425          case MouseEvent.MOUSE_DRAGGED:
426          case MouseEvent.MOUSE_ENTERED:
427          case MouseEvent.MOUSE_EXITED:
428          case MouseEvent.MOUSE_WHEEL:
429          case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED:
430          case InputMethodEvent.CARET_POSITION_CHANGED:
431              consumed = true;
432              break;
433          default:
434              // event type cannot be consumed
435        }
436    }

199                }
200            }

//처리할 이벤트가 아니면.. 무한루프 돌면서 시간 때우기.. (물론 스레드임으로 인터럽트 발생하면 
//다른 스레드에게 실행권 넘어감. 
201            while (eventOK == false);
202
203            if (eventLog.isLoggable(PlatformLogger.FINEST)) {
204                eventLog.finest("Dispatching: " + event);
205            }
206
//아래 부분이 핵심인듯 한데.. 이해가 안됨.. 저 메서드들도 숨어있고..
207            Object handle = null;
208            if (delegate != null) {
209                handle = delegate.beforeDispatch(event);
210            }
211            eq.dispatchEvent(event);
212            if (delegate != null) {
213                delegate.afterDispatch(event, handle);
214            }
215
216            return true;
217        }
218        catch (ThreadDeath death) {
219            threadDeathCaught = true;
220            return false;
221
222        }
223        catch (InterruptedException interruptedException) { //프로그램 내의 모든 스레드를 회수 = 프로그램 종료
224            return false; // AppContext.dispose() interrupts all
225                          // Threads in the AppContext
226
227        }
228        catch (Throwable e) {
229            processException(e);
230        }
231
232        return true;
233    }


**************************************************************


235    private void More ...processException(Throwable e) {
236        if (eventLog.isLoggable(PlatformLogger.FINE)) {
237            eventLog.fine("Processing exception: " + e);
238        }
239        getUncaughtExceptionHandler().uncaughtException(this, e); //분석하고 싶은데 메서드 못찾겠음..
240    }




***************************************************************


//우선순위큐로 이벤트들을 모아놓고, 스레드 세이프하게 fifo방식으로 처리
101 public class More ...EventQueue {
102 
103     // From Thread.java
104     private static int threadInitNumber;
105     private static synchronized int More ...nextThreadNum() {
106         return threadInitNumber++;
107     }
108 
109     private static final int LOW_PRIORITY = 0;
110     private static final int NORM_PRIORITY = 1;
111     private static final int HIGH_PRIORITY = 2;
112     private static final int ULTIMATE_PRIORITY = 3;
113 
114     private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1;
115 
116     /*
117      * We maintain one Queue for each priority that the EventQueue supports.
118      * That is, the EventQueue object is actually implemented as
119      * NUM_PRIORITIES queues and all Events on a particular internal Queue
120      * have identical priority. Events are pulled off the EventQueue starting
121      * with the Queue of highest priority. We progress in decreasing order
122      * across all Queues.
123      */
124     private Queue[] queues = new Queue[NUM_PRIORITIES]; //스레드 우선순위별로 관리하는듯
125 
126     /*
127      * The next EventQueue on the stack, or null if this EventQueue is
128      * on the top of the stack.  If nextQueue is non-null, requests to post
129      * an event are forwarded to nextQueue.
130      */
131     private EventQueue nextQueue; //자신을 가리켜? 뭐지?? 연결리스트인가? 이벤트큐 연결 리스트 자료구조?
132 
133     /*
134      * The previous EventQueue on the stack, or null if this is the
135      * "base" EventQueue.
136      */
137     private EventQueue previousQueue; //이전을?? 
138 
139     /*
140      * A single lock to synchronize the push()/pop() and related operations with
141      * all the EventQueues from the AppContext. Synchronization on any particular
142      * event queue(s) is not enough: we should lock the whole stack.
143      */
144     private final Lock pushPopLock; //스레드 세이프 (동시에 pop 불가 = 하나의 이벤트당 하나의 스레드)
145     private final Condition pushPopCond;
146 
147     /*
148      * Dummy runnable to wake up EDT from getNextEvent() after
149      push/pop is performed
150      */
151     private final static Runnable dummyRunnable = new Runnable() {
152         public void More ...run() {
153         }
154     };

생략..



*****************************************************************8


249    private static class More ...HierarchyEventFilter implements EventFilter {
250        private Component modalComponent;
251        public More ...HierarchyEventFilter(Component modalComponent) {
252            this.modalComponent = modalComponent;
253        }
254        public FilterAction More ...acceptEvent(AWTEvent event) {
255            if (modalComponent != null) {
256                int eventID = event.getID();
257                boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
258                                     (eventID <= MouseEvent.MOUSE_LAST);
259                boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
260                                      (eventID <= ActionEvent.ACTION_LAST);
261                boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
262                /*
263                 * filter out MouseEvent and ActionEvent that's outside
264                 * the modalComponent hierarchy.
265                 * KeyEvent is handled by using enqueueKeyEvent
266                 * in Dialog.show
267                 */
268                if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
269                    /*
270                     * Modal internal frames are handled separately. If event is
271                     * for some component from another heavyweight than modalComp,
272                     * it is accepted. If heavyweight is the same - we still accept
273                     * event and perform further filtering in LightweightDispatcher
274                     */
275                    return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
276                }
277                if (mouseEvent || actionEvent || windowClosingEvent) {
278                    Object o = event.getSource();
279                    if (o instanceof sun.awt.ModalExclude) {
280                        // Exclude this object from modality and
281                        // continue to pump it's events.
282                        return FilterAction.ACCEPT;
283                    } else if (o instanceof Component) {
284                        Component c = (Component) o;
285                        // 5.0u3 modal exclusion
286                        boolean modalExcluded = false;
287                        if (modalComponent instanceof Container) {
288                            while (c != modalComponent && c != null) {
289                                if ((c instanceof Window) &&
290                                    (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
291                                    // Exclude this window and all its children from
292                                    //  modality and continue to pump it's events.
293                                    modalExcluded = true;
294                                    break;
295                                }
296                                c = c.getParent();
297                            }
298                        }
299                        if (!modalExcluded && (c != modalComponent)) {
300                            return FilterAction.REJECT;
301                        }
302                    }
303                }
304            }
305            return FilterAction.ACCEPT;
306        }
307    }


by givingsheart 2014. 1. 2. 09:27


툴작업중 내 서브프레임들 (dbinfo, ddl, dml, dql, console만 빼고) 각각이 사용자가 입력한 문자열들을 save/load 지원을 
해줘서 xml파일로 관리하기로 마음먹고.. 지난번에 작업해둔.. property.loadFromXml인가를 사용하려 하다가.. 

propertyEditor라는 것을 접하게 되었고 검색을 해보았다. 이번 작업도.. 스프링과 매치되는 부분이 있다니..
황당할 따름이다. 툴 작업이라.. 스레드에 전혀 관심을 안두었던 내게.. 약간의 찜찜함을 남겨준다.

property를 이용해 xml로 쏘고 읽는건 해봤으니.. 약점이 있더라도 propertyEditor를 적용해봐야겠다.

구글에서 propertyeditor sample or example 로 검색을 해봐야겠다. 아..서점도 가야하는데..

스프링 jre 다운 받기


(토비라는 유명한 분 블로그인듯)


Example 10.6: The YesNoDialogAlignmentEditor Class

package oreilly.beans.yesno;
import java.beans.*;
import java.awt.*;

public class YesNoDialogAlignmentEditor extends PropertyEditorSupport 
{  
// These two methods allow the property to be edited in a dropdown list.  
// Return the list of value names for the enumerated type.  

public String[] getTags() 
{    
return new String[] { "left", "center", "right" };  
}  
// Convert each of those value names into the actual value.  

public void setAsText(String s) 
{    
if (s.equals("left")) setValue(new Integer(YesNoDialog.LEFT));    
else if (s.equals("center")) setValue(new Integer(YesNoDialog.CENTER));    
else if (s.equals("right")) setValue(new Integer(YesNoDialog.RIGHT));    
else throw new IllegalArgumentException(s);  
}  
// This is an important method for code generation.  

public String getJavaInitializationString() 
{    
switch(((Number)getValue()).intValue()) 
{    
default:    
case YesNoDialog.LEFT:   return "oreilly.beans.yesno.YesNoDialog.LEFT";    
case YesNoDialog.CENTER: return "oreilly.beans.yesno.YesNoDialog.CENTER";    
case YesNoDialog.RIGHT:  return "oreilly.beans.yesno.YesNoDialog.RIGHT";    }  
}

}

위를를 보니 어떻게 써야할지.. 약간 감은 잡겠다
클래스 PropertyEditorSupport 그중.. 내가 써봐야 할것은.. 아래 메서드 이리라..

PropertyEditor 는, 프로퍼티치를 편집하는 완전한 커스텀 컴퍼넌트를 제공할 수가 있습니다. PropertyEditor 는, 에디터의 컴퍼넌트와 제휴해, PropertyChange 이벤트를 트리거해 프로퍼티치의 변경을 통지합니다.

getCustomEditor 를 호출하는 고레벨인 코드에서는, 컴퍼넌트를 보다 큰 프로퍼티 시트에 짜넣거나 독자적인 다이얼로그에 배치하거나 할 수가 있습니다.


정의:
인터페이스 PropertyEditor 내의 getCustomEditor


반환값:
사용자가 현재의 프로퍼티치를 직접 편집할 수 있도록(듯이) 한다 java.awt.Component. 지원되지 않는 경우는 null



'자바se api > swing' 카테고리의 다른 글

스윙..  (0) 2014.01.02
스윙의 기본적인 멀티스레드 처리방식  (0) 2014.01.02
이벤트처리 스레드;;  (0) 2014.01.02
자바 스윙 겁나 복잡하고 방대함...  (0) 2014.01.02
by givingsheart 2014. 1. 2. 09:24

간단하게 swing으로 구현? 이 말이 부끄러워진다..


예전에 간단하게 awt gui 기반에서 text 필드에 사용자가 디비 드라이버 정보, 접속url, id,pw 등을 입력하고 버튼을 
누르면, 모든 컴포넌트의 값을 읽어와 루트element에 setproperty 하는 형식으로 값들을 넣고.. 그걸 documnet에 넣어서
dbinfo.xml로 출력해주는 간단한 코드였다.

사실 awt의 기능들을 충분히 사용해보지 않은 상태로(예전엔 awt 클래스들을 디자인 패턴의 관점에서 분석만 했었고..)

현재 제대로 된 dbConnect정보,ddl,dml,dql 을 사용자가 입력하고, 실제 db와 연동시키려는 툴을 만들기 위해
툴의 디자인을 고민했었다. 

1.처음의 기본 디자인은.. 각각의 입력(커넥션,ddl,dml,dql) 페이지를 JPanel로 구분해서.. 메인 툴 프레임에 
예전에 했던대로 gridLayout을 설정해 위에서부터 순서대로 판넬을 포함 시키는 방법. add(component)

(여기서 문제가 발생. 메인 프레임이 800,800 사이즈라.. 각각의 서브 판넬이 800,200 식으로 나눠 가졌으나.. 
개별적 작업 공간이 크기도 부족했을 뿐더러.. 작업 공간이 섞여있어서 직관적이지 못했다.)

2.두번째 디자인은 메인 툴 프레임에  <-  -> 버튼을 추가해서 해당 페이지로 이동하고 작업하는 방식이었다.

첫번째 문제가 현재 겹쳐있는(위치값 동일) 패널에 대해 ->버튼을 클릭시 위치를 이동시켜서 화면 밖으로 
빼고, 다음 패널이 사용자의 입력을 받을 수 있게끔 하는 것이었는데.. 포커스 문제,위치 이동과 복귀등 생각보다 
복잡했다.(물론 시간 투자해 억지로 매달리면 구현은 가능했을테지만..)

두번째 문제가 툴 사용자가 실제 db정보를 입력하고 그것이 제대로 작동하는지 피드백을 받아볼 필요가 있었고
ex) "127.0.0.1 oracal xxx 에 접속 성공"  "create memberInfo( id number(3), name vchar2(20), address ...)
memberInfo 테이블 생성 성공"  "selete * memberInfo 성공" "결과 001,가길동,강북  002,나길동,강남" ...

이 처리를 위해서 이클립스의 콘솔창의 개념이 필요했다. (현재까지 총 5개의 구분되는 컨테이너 컴포넌트=판넬 필요)
+ 콘솔용 판넬은 db커넥션 정보 입력용 패널, ddl 입력용 패널, dml 입력용, dql 입력용 패널에서 공유되어야할 필요가
있었다.

3.그 다음 디자인이.. 메인 프레임 안에.. 각각 독립적인 정보입력용 프레임 4개 + 결과 출력용 프레임 1개를 넣는 방식
이었다. mainFrame.add(new MyFrame("ddl")); 요런 식으로.. (물론... 실행을 시키면 프로그램 창(프레임)이 2개나 
띄워졌지만.. 뻑=익셉션이났다.. 디버깅을 안해서 정확한 이유는 모른다. OTL)

나는 여전히 3번 방식이 객체 지향적이며 무엇보다 내가 원했던.. 사용자가 작업을 하면서 명확해질수 있게끔 돕는 것이
라 생각해.. 이 아이디어를 포기 할수 없었다. 그렇기에 정보를 찾아봤다.

4.LayerdPane 개념 습득. 
swing에 관하여 조금 더 인터넷 서치를 하던중..(내실 없이 아주 거지같은 광고용 블로그에도 당하고.. 욕나옴 -_-;)
알게된 개념으로서.. 아주 오래전 2d 슈팅or액션 게임 만들던때 사용하던 2d화면에 배경별(멀고,가깝고),오브젝트별,캐릭터별 
층을 구분하고 아래부터 먼저 draw 해주는 개념이었다. 

이걸 이용한다면.. 콘솔창의 문제를 제외하곤 애초에 생각했던 2번문제.. <- -> 버튼으로 작업공간 이동이 쉽게 풀릴듯 하여.. 작업을 시작하려 하였으나..

5.Intenal(내부)Frame 개념 습득..
요 클래스가 내가 3번에서 뻑이 났던.. 문제를 해결해주는 개념이었다.. (나처럼 메인 프레임 안에.. 독립적인 프레임을
만들길 원하는 수요가 있어서 java가 만들어 줬나보다. 

멋도 모르고 JFrame을 상속받은 내 메인 프레임에 ddl = new JInternalFrame(); mainFrame.add(ddl); 
요런식으로 해봤더니.. 원하던대로.. 뻑도 안나고 내부의 독립적인 프레임이 생성이 되었다. (이때의 감동이란 ㅠㅠ)

6.샘플 소스코드 입수
... 그렇다.. 이미 java의 고급 개발자들이 우리에게 은혜를 베푸시어.. core api 외에도 샘플 소스코드를
만들어주셨는데.. 왜 이생각을 못했을까? (예전에 한번, 콘솔용 겁나 복잡해진 사용자 컨트롤 처리 로또 프로젝트를 실패
하고, 하루만에 접은 gui 기반 동물 농장 프로젝트를 통해.. "다음에는 샘플 코드부터 작업을 시작하자고 생각했었는데.."

채팅이나, nio 채팅의 경우도.. 거의 맨땅에서 작업하느라 효율이 안좋았는데.. 왜 그랬을까?.. 왜그랬을까..?

에서.. internalFrame 샘플 코드를 받아본 나는.. 다시 한번 새로운 충격에 빠졌다..


7.방황하는 시간..

아래처럼 구현하는 것이.. 스윙 패키지의 기능을 90% 이상 뽑아내는 것이리라..
(내가 구현했던 것들이.. 아주 먼지처럼 느껴졌다.. 내가 한건.. gui 패키지 사용의 겉핥기..)

import javax.swing.JInternalFrame;
import javax.swing.JDesktopPane;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JMenuBar;
import javax.swing.JFrame;
import javax.swing.KeyStroke;

import java.awt.event.*;
import java.awt.*;

/*
 * InternalFrameDemo.java requires:
 *   MyInternalFrame.java
 */
public class InternalFrameDemo extends JFrame
                               implements ActionListener {
    //내경우 JFrame을 상속받아.. add 처리만 하려 했는데.. 아래의 JDesktopPane 이란 관리용 클래스...
    //내가 구현하려던 mainFrame 클래스는 각각의 서브 프레임(db입력용,dml,ddl,dql,콘솔창용)에 대해
    //중앙 컨트롤(사용자의 개별적 입력 이벤트를 전달받아.. 처리후 각각의 서브 프레임에게 명령 전달 하는 
    //메디에이터 클래스의 개념.. ) 한마디로 씬or 페이지 매니저를 만들려고 했었는데.. 이미 만들어져 있었다.
    //즉.. 메인 프레임에서 컨테이너 관리적인 측면을 또 다른 객체에게 위임한 상태..
    //글고 중요한건 아니지만.. 내경운 지금까지 해당 컴포넌트에 대한 리스너를 component.addXXListerner( new
    // xxxAdapter(){익명 클래스 기법으로 재정의}; ) 식으로 처리했는데.. 위처럼 implements를 시키는 것에 대해
    // 고민이 필요할듯 하다.
/*
<명세>

멀티 문서 인터페이스 또는 가상 데스크탑을 생성하는 컨테이너입니다. JInternalFrame 객체를 생성해,JDesktopPane 에 추가합니다. JDesktopPane  JLayeredPane 를 확장해, 오버랩의 가능성이 있는 내부 프레임을 관리합니다. 또, 현재의 Look & Feel (L&F)에 대해서 UI 클래스에서 설정된 DesktopManager 의 인스턴스에의 참조도 유지합니다. JDesktopPane 는 경계를 지원하지 않습니다.  

이 클래스는 일반적으로,JInternalFrames 의 부모로서 사용되어 플러그 인 가능한 DesktopManager 객체를JInternalFrames 에 제공합니다. L&F 별로 구현되는 installUI 로,desktopManager 변수의 적절한 설정을 실시합니다. JInternalFrame 의 부모가 JDesktopPane 의 경우, 클로즈나 사이즈 변경등의 동작의 대부분을 desktopManager에 위양 합니다.  

상세와 사용예에 대해서는, 「The Java Tutorial」의「How to Use Internal Framesy」를 참조해 주세요.  

*/

    JDesktopPane desktop; //요것을 멤버 필드로 보관하는 이유는 좀 있다가..

    public InternalFrameDemo() {
        super("InternalFrameDemo");

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
       //내경운 일단 빠른 구현을 위해서 setSize인가로 800,600을 떄려 넣었다. 아래가 정석이다.ㅠㅠ
       //툴킷의 경운 .. 사용자의 native한 gui 데스크탑 환경이라 생각하면 된다.(os)
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 
        setBounds(inset, inset,
                  screenSize.width  - inset*2,
                  screenSize.height - inset*2);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        //메인프레임을 클래스 멤버필드로 관리하기에.. 아래처럼 서브프레임을 만드는 행위를 메서드로 뺄수 있게 되었다.
        //서브프레임을 동적으로 넣었다, 뺐다 할수 있다.  미리 고정된 .. mainFrame.add(subFrame1); ... 요럴 필요가 없음.
        //생성하려는 서브 프레임이 동일하다면.. 아래처럼 쓰면 될테고.. dml,dql,,ddl,console,dbinfo처럼 제각각 특징이 
        //있다면 상속받아서.. 다형성을 이용해야 한다.
        createFrame(); //create first "window" 
        
        //메디에이터? 관리용 객체(JDesktopPane)을 만들었으니 메인 Frame에 세팅하는 작업이 필요.
        setContentPane(desktop);
        //아래의 경운.. 내가 만들툴의 디자인상 각각의 서브 프레임이 사용자의 입력 데이터를 개별적으로 save/load
        //하는데 사용이 가능할듯. JMenuBar crateMenuBar()로서 메서드 단위로 개념을 분리시켰다.
        setJMenuBar(createMenuBar());

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
    }

    protected JMenuBar createMenuBar() {
        //이처럼 모듈별로 쪼개는것은 중요하다. 내가 구상한 디자인 경우에도.. ddl 프레임에서 setColumn() 이란 메서드로서
       // 반복적이고 몇개가 될지 모르는 테이블의 컬럼 정의를 메서드로 추출하려 했었으니까..
       //ex)사용자가 테이블에 대해 하나의 컬럼 정의를 완료하고 add 버튼을 누르면 다음 컬럼 정의할수 있고
       //또는 delete를 누르면 이미 정의된 컬럼 날아가고.. confirm 누르면 확정되어 데이터를 실제로 조립하고...
        JMenuBar menuBar = new JMenuBar();

        //Set up the lone menu.
        //정말..멋진 디자인이다. 메뉴바 객체에 메뉴들을 추가한다.. 컨테이너의 개념...
        JMenu menu = new JMenu("Document");
        
       //놀라울 따름이다.. 해당 메뉴객체에 키보드 alt+D 이벤트를 연결하는 작업이 이처럼 간단하다.
        menu.setMnemonic(KeyEvent.VK_D);
        menuBar.add(menu);

        //Set up the first menu item.
        //마찬가지이다. 하나의 메뉴객체는 또한 내부에 이것 저것 아이템을 가지고 있을테니.. 나는 정말로
        //자바가 제공하는 gui 프레임 워크를 1%도 이해를 못하고선.. 혼자서 대충 만들수 있다느니..하고 자만했다.
        //얼마나 직관적이고 일관성있는 인터페이스 인가?
        JMenuItem menuItem = new JMenuItem("New");
        menuItem.setMnemonic(KeyEvent.VK_N);
        //일반적인 키 이벤트야 비트마스크로 추출하는건 알고 있으니 넘어가고..
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_N, ActionEvent.ALT_MASK));

        //아래의 menuItem.setActionCommand("new"); menuItem.addActionListener(this);... 화룡점정이다.
        //내가 만들던 파일매니저의 서브시스템중 워커 팩토리를 구현할때 다형적인 워커와 클래스를 해쉬맵으로 
        //key,value로 묶어서 문자열로 매핑한 것을 상기하자.  

/* 리마인드용 내가 구현했던 코드 조각
workerMap = new HashMap<String,Class<?>>();
Properties pro = new Properties();
//TODO:경로까지 지정을 할까;;
pro.loadFromXML(new FileInputStream("worker.xml"));
for(Entry<Object,Object> entry : pro.entrySet())
{
try
{
workerMap.put((String)entry.getKey(), Class.forName((String)entry.getValue()));
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}


public FileWorkerImpl getWorker(String workerName)
{
assert workerName != null : "workerName 뷁:" + workerName;
assert !" ".equals(workerName): "workerName 뷁:" + workerName;
FileWorkerImpl worker = null;
try
{
worker = (FileWorkerImpl)workerMap.get(workerName).newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return worker;
}
*/
        //다시 생각해봐도 정말 아름다운 디자인으로, 커맨드 패턴 + 옵저버 패턴(이벤트 디스패처+리스너)의 멋진 활용
        //내가 전에 최고 수준의 코딩 기법을 따라하고 싶다고 노래를 불렀었는데.. 자바 샘플 소스중.. swing 포함 gui
        //부분.. 정말로 멋지다. 내가 아래처럼 설계하고 구현할수 있게끔 훈련된다면 얼마나 좋을까 ㅠㅠ 샘플 소스 코드
        //매일 하나씩 분석해야겠다.
        menuItem.setActionCommand("new");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        //Set up the second menu item.
        //중복 됨으로 분석 생략
        menuItem = new JMenuItem("Quit");
        menuItem.setMnemonic(KeyEvent.VK_Q);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_Q, ActionEvent.ALT_MASK));
        menuItem.setActionCommand("quit");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        //메뉴바 란.. 하나의 프레임 속에선 서브 컴퍼넌트이지만.. 그 자신도 내부에 메뉴들과 그에 속한 아이템들을
        //갖는 객체.. 이 메뉴바란 객체를 구성하는걸 메서드 단위로 뺀 모듈화.. 배워야한다..ㅠㅠ 
        return menuBar;
    }

    //React to menu selections.
    //위에서 작업해준.. 액션이벤트란 커맨드를 문자열로 매핑하고, 액션 리스너 클래스의 actionPerformed() 메서드를
    //재정의 하여.. 아래처럼 간단하게 구현이 가능하다. 명령에 따른 처리를 메서드 단위로 추출한 것도 포인트.
    //사실.. 아래의 작업을 더 추상화하면.. 내가 파일시스템에 구현했듯.. 팩토리 + 워커를 상속계층으로 만들어
    //동일한 인터페이스인 work()로서 추상화하면.. 더 간단해진다. 
    //ex) Worker worker =  workerFactory.getInstance().getWorker("copy");
    //      Work work = workFactory.getInstance().getWork("file", srcPath, destPath);
    //      worker.setWork(work);  한마디로.. 디자인 패턴에 빠져.. 워커도 추상화, 워크도 다형적으로 추상화 해버렸다..
    //      worker.work();
    // 근데 실제로 위처럼 구현하게 되면.. 코드는 이뻐지지만, 명시적이지 않다. 예전에 코드컴플리트에서도 지적했듯
    // switch() case 문 또는 if else() 문으로 아래처럼 명시적으로 처리가 되면.. 추상화할 필요가 없다는데.. 동의한다.
    // 아래는 참으로 명시적이다.
    public void actionPerformed(ActionEvent e) {
        if ("new".equals(e.getActionCommand())) { //new
            createFrame();
        } else { //quit
            quit();
        }
    }

    //Create a new internal frame.
    protected void createFrame() {
        MyInternalFrame frame = new MyInternalFrame();
        frame.setVisible(true); //necessary as of 1.3
        //아까 JDeskTopPane(서브 프레임들의 메디에이터)을 멤버 필드로 보관한 효과가 여기서 나타남..
        desktop.add(frame);
        try {
            frame.setSelected(true); //요건 정확힌 모르겠으나.. 해당 프레임에 포커스 이동시키는 걸테지..
        } catch (java.beans.PropertyVetoException e) {} //요 빈 어쩌구 예외 클래스는 당황스럽다.. 지금은 걍 패스
    }

    //Quit the application.
    protected void quit() {
        System.exit(0);
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread. 
       위의 의미는 정확히 이해는 못하겠다.
       단지 이벤트 디스패팅 스레드란 단어를 보니.. 예전에 컴포넌트에 이벤트가 발생햇다는 것을 감지하는 이벤트 디스패처
       (내가 awt 분석떄 자주본 놈.. eventmulitcaster였나?)이 떠오른다. 
       대충 해석을 하면.. 멀티스레딩에 안전하게 gui객체를 생성하기 위해서 이벤트 감시자에게 불려가는 콜백 메서드를
       정의하는것 같은데.. 생소할 따름이다.
     */
    private static void createAndShowGUI() {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        InternalFrameDemo frame = new InternalFrameDemo();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Display the window.
        frame.setVisible(true);
    }

//아래를 보니.. 이해가 된다.. 위의 creatrAndShowGUI()가 특별한 의미가 있었던건 아닌.. 걍 init()메서드
//였구나..
//그보다 더욱 중요한건.. invokerLater의 매개변수로 스레드(=Runnable() + run()재정의를 익명클래스 기법을
//통해 생성과 동시에 재정의 처리) 객체를 생성하고.. run() 내부에서 InternalFrameDemo 란 gui 객체를 생성했다는 점이다.
//만약 내가 툴 프로그램을 멀티스레딩으로 처리했다면..  JFrame을 상속받은 툴 프로그램 객체에 Runnalbe을
//implements 하고 내부에 Thread 객체를 둔 다음에.. 생성자에서 thread = new Thread(this); start(){thread.start();}
//run(){} 을 구현했을 것이다. 위와 비교해 아래는 얼마나 깔끔한가..(제대로 이해는 못했지만..)
    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}


ps.음.. 일단.. 멋진 코드를 보면서 감동도 받았지만, 현재 내 초라한 능력에 위축도 된다.. 툴 프로그래밍 진도가 예상보다 
늦어질지도 모른다.(자바가 제공하는 패키지를 백분 활용하여 작업을 한다는 것의 난이도를 느꼈기 때문이다.) 위와 같은 코딩 
스타일을 몸에 익혀야 할것이다. 

불행중 다행이라면.. 아직 완전히 이해는 못했지만.. 학원 1달차 부터.. 꾸준히 디자인 패턴 관련 글과 자료를 공부해 놓아서
여러 패턴들이 눈에 보이고... 왜 그렇게 설계를 했을지.. 약간의 짐작과 이해가 가능하다는 점이다. 그런고로 app를  oop를 
활용해 제대로 구현하기 위해선 디자인 패턴에 대한 이해는.. 선택이 아닌, 필수이다. 디자인 패턴은 개발중의 여러 복잡한 
이슈를 해결하기 위한 솔루션이다.

ps2.오늘 점심땐 중고 서점 가서 db책,swing책,멀티스레드책(+@스프링)


'자바se api > swing' 카테고리의 다른 글

스윙..  (0) 2014.01.02
스윙의 기본적인 멀티스레드 처리방식  (0) 2014.01.02
이벤트처리 스레드;;  (0) 2014.01.02
propertyEditor vs ConversionService + 토비의 스프링?  (0) 2014.01.02
by givingsheart 2014. 1. 2. 09:11
| 1 |