2013년 12월 27일 금요일

Reactor 패턴

Reactor pattern

reactor pattern 에 대해서 알아보자. Node.js 에서 Event-driven network programming 이 유명해 졌는데, 찾아보니 이미 여러개의 framework 들이 존재했다. 그런데 이런 pattern 을 Reactor pattern 이라고 해서, Reactor pattern 에 대해 찾아보고 설명을 몇자 적어놓는다.

정의
일단 정의를 보면,

서비스를 처리하는 쪽(서버라고 생각하면 될 듯 하다.)으로 동시에 요청이 들어온 service request 를 처리하기 위한 event handling pattern 이라고 한다.
이 때 서버에서는 들어온 요청들을 분류(demux)해서 이 녀석들을 적절한 request handler 에 dispatch 한다. 이 때 동시에 여러개의 요청을 처리하는 것이 아니라, 하나에 대한 처리를 끝내고 그 다음 것을 처리하는 방식으로 하나씩 처리한다.
The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.[ref. 1]
이런 Reactor pattern 을 이용한 여러 web server 가 있는데, Node.js 도 그중에 하나이다. 이외에 여러 개들이 있는데, ref. 1에서 확인하자.

기존의 Design


from : ref. 3
기존의 ThreadPool 을 이용하는 경우를 고려 해 보자.
  1. ServerSocket 으로 request A 가 들어오면 Thread 를 할당 해 준다.
  2. 그럼 이 Thread 는 그 socket 을 가지고 read, write 작업(IO) 등을 할 것이다. 그런데 이 와중에 ServerSocket 에 request B가 들어오면, context switching 이 일어난다.
  3. 그럼 새로 들어온 request 에 대해 Thread 를 배분해주고, 또 이 socket 으로 read()/write() 등의 작업을 할 것이다.
  4. 그러면서 A 의 작업을 하기 위해 중간에 다시 context switching 하고, 그러다 다시 B 작업하려고 context switching 하면서 IO 작업을 모두 처리할 것이다.

그런데 reqest A 가 들어와서 Thread 가 만들어진 후에 또다른 reqeust 가 들어오지 않는 상황을 가정 해 보자.

그럼 request A 를 처리하다가 중간에 request 가 들어왔는지 확인하기 위해 context switching 을 해야 한다. 만약 들어왔으면 Thread 를 만들어주고, 그렇지 않으면 다시 request A 를 처리하러 돌아갈 것이다.

그런데 이렇게 확인하는 시간이 아깝다.

codes

class Server implements Runnable {
   public void run() {
       try {
           ServerSocket ss = new ServerSocket(PORT);
           while (!Thread.interrupted())
               new Thread(new Handler(ss.accept())).start();
        // or, single-threaded, or a thread pool
       } catch (IOException ex) { /* ... */ }
   }
   static class Handler implements Runnable {
       final Socket socket;
       Handler(Socket s) { socket = s; }
       public void run() {
           try {
               byte[] input = new byte[MAX_INPUT];
               socket.getInputStream().read(input);
               byte[] output = process(input);
               socket.getOutputStream().write(output);
           } catch (IOException ex) { /* ... */ }
       }
       private byte[] process(byte[] cmd) { /* ... */ }
    }
}

Reactor Pattern

그래서 나온 것이 Reactor Pattern 이라고 한다.[ref. 2]

Reactor 는 우리나라 말로 하면 "반응로" 이다. 그러니까 event 가 발생하면 반응하는 녀석이다.

Reactor 는 event 가 발생하기를 기다리고, event 가 발생하면 Reactor 는 이 event 를 적절한 event handler 에 넘겨주는 역할을 하게 된다.


********

여기서 부터는 ref. 2 의 내용을 번역하고 각색했다.

ref. 3 에서는 java code 로 Reactor pattern 을 설명해 주는 ppt 이다. ref. 3 의 코드를 바탕으로 설명할 것이다. 참고로 이 java code 는 non blocking io library 인 nio package 를 사용한다.

Reactor pattern 에 중요한 2가지 요소가 있다. 하나는 event 를 받고 전달해주는 Reactor 와 다른 하나는 Reactor 가 보낸 event 를 실제로 처리하는 Handler 가 있다. ref. 3 에서 UI programming 과 연관지어서 설명한 것을 인용하면,
  • Reactor 는 UI 를 처리하는 thread 이고,
  • Handlers 는 UI의 ActionListeners 라고 생각하면 된다.
다시 얘기하면,
  • Reactor 로 event 가 들어오면 알맞는 handler 로 dispatch 주는 역할을 담당하고, 
  • Handler 는 이 dispatch 된 event 를 받아서 처리하는 역할을 하게 된다.
이 추상적인 이야기를 ref. 3 에 있는 java 코드를 통해서 확인하자.

Sigle-threaded codes
먼저, 하나의 thread 를 사용해서 동작하는 것만 고려하자.

소스코드를 이해하기 위해서 몇가지 class 에 대한 이해를 해야 한다.
  1. Channels :non-blocking read 를 지원해 주는 녀석이다.
  2. Selector : Selector 는 어느 channel set 이 IO event 를 가지고 있는지를 알려준다. Selector.select() 는 I/O 가 발생한 channel 을 return 해주게 되는데, return 할 channel 이 없다면 계속 기다리게 된다.(block). 이 block 된 녀석을 바로 return 시켜주는 녀석이 Selector.wakeup() 이다.
    Selector.selectedKeys() 는 Selection Key 를 return 해 준다. Reactor 는 이 Selection Key 를 보고 어떤 handler 로 넘겨줄 지를 결정한다.
  3. SelectionKeys : SelectionKey 는 Channel 과 Handler 에 대한 reference 를 가지고 있다. ServerSocketChannel 에 selector 를 등록하면 key 를 준다. 이 key 가 SelectionKey 이다.
  4. Acceptor : Acceptor 는 handler 다. connection 을 만들기 위한 special handler 이다.


final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ...
   
    Handler(Selector sel, SocketChannel c) throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }
    ...
}

Reactor 동작 : Acceptor 등록

Reactor 는 event 가 들어오면, ServerSocketChannel 을 만들기 위해 accept 를 해야 한다.

class Reactor implements Runnable {
    final Selector selector;
    final ServerSocketChannel serverSocket;

    Reactor(int port) throws IOException {
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(
                new InetSocketAddress(port));
        serverSocket.configureBlocking(false);
        SelectionKey sk =
                serverSocket.register(selector,
                        SelectionKey.OP_ACCEPT);
        sk.attach(new Acceptor());
    }

그러므로 SelectionKey 0 는 Acceptor 에 묶이게 된다.(Bind 된다.)
Acceptor 는 connection 을 만들기 위한 special handler 이다.
Reactor 가 SelectionKey 0 를 보고 Acceptor 에게 넘겨야 겠다는 것을 알 수 있다.
  • SelectionKey sk = serverSocket.register(selector,SelectionKey.OP_ACCEPT);
이 부분을 통해서 selector 와 channel 을 함께 등록을 하고, return 되는 SelectionKey 가 OP_ACCEPT 에 반응하도록 설정한다.

Reactor 동작 : dispatch
public void run() { // normally in a new Thread
        try {
            while (!Thread.interrupted()) {
                selector.select();
                Set selected = selector.selectedKeys();
                Iterator it = selected.iterator();
                while (it.hasNext())
                    dispatch((SelectionKey) (it.next());
                selected.clear();
            }
        } catch (IOException ex) { /* ... */ }
    }

Selector 는 IO event 를 계속해서 찾는다.

Selector.select() 를 호출한 후 selectedKeys() 를 통해 pending 된 event 가 있는 Channel 들의 SelectionKey 들을 가져올 수 있다.

이 때 SelectionKey 0 가 있으면, ServerSocketChannel 에 event 가 발생했다는 의미이고, Reactor 는 이 event 에 대한 connection 을 만들기 위해 Acceptor 에 보낸다.

Acceptor 동작

class Acceptor implements Runnable { // inner

        public void run() {
            try {
                SocketChannel c = serverSocket.accept();
                if (c != null)
                    new Handler(selector, c);
            } catch (IOException ex) { /* ... */ }
        }
    }


final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ...
   
    Handler(Selector sel, SocketChannel c) throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }
    ...
}


그렇게 Acceptor 가 client 1 의 connection 에 대한 channel 인 ServerSocketChannel 1 을 만들게 된다. 그리고 나서 Selector 를 ServerSocketChannel 1 에 등록하고 SelectionKey 를 얻고, 이 key 에 handler 를 attach 한다.

여기서
  • interestOps(SelectionKey.OP_READ)
를 통해서 selector 가 이 selectionKey 를 READ event 가 발생했을 때만 return 하도록 설정해 준다. 쉽게 얘기하면, 이 selectionKey 와 관련된 event type 을 정해주는 것이다.

이제 client 는 이 ServerSocketChannel 1을 이용해서 read from server / write to server 를 할 수 있다. connection 은 Acceptor 를 통해 이미 했기 때문에 이제는 필요없다.

그러고나서 sel.wakeup()[ref. 7] 을 호출하면, select()(code 에서는 selector.select() 가 될 것이다.) 가 바로 return 할 수있는 channel 이 없어도 바로 return 해 주고 그로 인해 blocked 상태에서 벗어나서 thead 가 다시 깨어나게 된다.



정리

정리해서 다시 이야기 하면, Server 는 Selector.select() 를 호출한다.
  • 이 때 SelectionKey 1 이 return 된다면, 이 것은 ServerSocketChannel 1 이 event 를 가지고 있다는 이야기가 되고, Reactor 는 이 SelectionKey1 이 bind 한 Handler 1 에 dispatch 해주게 된다.
  • 만약 SelectionKey 0 이 return 된다면, 이것은 ServerSocketChannel 0 에서 event 가 발생한 것이고, 새로운 client 가 접속한 것이다. 이 SelectionKey 0 는 Acceptor 에 bind 되어 있기 때문에 이 event 는 Acceptor 로 dispatch 된다.

"특정 ServerSocketCannel" 에서 "특정 event" 가 발생했을 때, 해당하는 SelectionKey 가 selector 에게 return 된다. 그러면 "selector 에서 가져온 SelectionKey" 가 가지고 있는 handler 를 실행한다.



여기까지 구현으로는 Reactor 는 어차피 Handler 의 작업이 끝나기를 기다려야 한다. selectionKey 하나를 다 처리하고 나야 다음 selectionKey 를 처리하게 된다. 우리는 이것을 극복하기 위해 multi-thread 를 이용할 수 있다.


codes

class Reactor implements Runnable {
    final Selector selector;
    final ServerSocketChannel serverSocket;

    Reactor(int port) throws IOException {
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(
                new InetSocketAddress(port));
        serverSocket.configureBlocking(false);
        SelectionKey sk =
                serverSocket.register(selector,SelectionKey.OP_ACCEPT);
        sk.attach(new Acceptor());
    }

    /*
    Alternatively, use explicit SPI provider:
    SelectorProvider p = SelectorProvider.provider();
    selector = p.openSelector();
    serverSocket = p.openServerSocketChannel();
    */
    public void run() { // normally in a new Thread
        try {
            while (!Thread.interrupted()) {
                selector.select();
                Set selected = selector.selectedKeys();
                Iterator it = selected.iterator();
                while (it.hasNext())
                    dispatch((SelectionKey) (it.next());
                selected.clear();
            }
        } catch (IOException ex) { /* ... */ }
    }

    void dispatch(SelectionKey k) {
        Runnable r = (Runnable) (k.attachment());
        if (r != null)
            r.run();
    }

    class Acceptor implements Runnable { // inner

        public void run() {
            try {
                SocketChannel c = serverSocket.accept();
                if (c != null)
                    new Handler(selector, c);
            } catch (IOException ex) { /* ... */ }
        }
    }
}

final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ByteBuffer input = ByteBuffer.allocate(MAXIN);
    ByteBuffer output = ByteBuffer.allocate(MAXOUT);
    static final int READING = 0, SENDING = 1;
    int state = READING;

    Handler(Selector sel, SocketChannel c)
            throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }

    boolean inputIsComplete() { /* ... */ }

    boolean outputIsComplete() { /* ... */ }

    void process() { /* ... */ }


    // class Handler continued
    public void run() {
        try {
            if (state == READING) read();
            else if (state == SENDING) send();
        } catch (IOException ex) { /* ... */ }
    }

    void read() throws IOException {
        int readCount = socket.read(input);
        if (readCount > 0) {
            process();
        }

        state = SENDING;
        // Normally also do first write now
        sk.interestOps(SelectionKey.OP_WRITE);
    }

    void send() throws IOException {
        socket.write(output);
        if (outputIsComplete()) sk.cancel();
    }
}

Multi-threaded
Handler 부분을 thread pool 을 이용해서 동시에 여러개를 처리할 수 있도록 한다.

ref. 9 에서는 상속을 이용해서 구현하였지만, 여기서는 차이점을 잘 알아볼 수 있도록 변경사항을 기존의 Handler 에 추가했다.

아래를 보면 PROCESSING 이라는 state 를 더 추가했다.

final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ByteBuffer input = ByteBuffer.allocate(MAXIN);
    ByteBuffer output = ByteBuffer.allocate(MAXOUT);
    static final int READING = 0, SENDING = 1;
    int state = READING;

    
    static ExecutorService pool = Executors.newFixedThreadPool(2);
    static final int PROCESSING = 2;
    

    Handler(Selector sel, SocketChannel c)
            throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }

    boolean inputIsComplete() { /* ... */ }

    boolean outputIsComplete() { /* ... */ }

    
    synchronized void process() { /* ... */ }
    


    // class Handler continued
    public void run() {
        try {
            if (state == READING) read();
            else if (state == SENDING) send();
        } catch (IOException ex) { /* ... */ }
    }

    
    synchronized void read() throws IOException {
        int readCount = socketChannel.read(input);
        if (readCount > 0) {
            
            state = PROCESSING;
            pool.execute(new Processer(readCount));
            
        }
        //We are interested in writing back to the client soon after read processing is done.
        selectionKey.interestOps(SelectionKey.OP_WRITE);
    }
    

    void send() throws IOException {
        socket.write(output);
        if (outputIsComplete()) sk.cancel();
    }

    
    synchronized processAndChageState(int readCount){
        process(readCount)
        state = SENDING;
    }
    
    class Processer implements Runnable {
        int readCount;
        Processer(int readCount) {
            this.readCount =  readCount;
        }
        public void run() {
            processAndChageState(readCount);
        }
    }
    
}

See Also

  1. Proactor Pattern 발표 자료
  2. proactor design pattern (번역),2012년 5월 18일 금요일
  3. http://www.tornadoweb.org/en/stable/

Reference
  1. http://en.wikipedia.org/wiki/Reactor_pattern
  2. http://jeewanthad.blogspot.kr/2013/02/reactor-pattern-explained-part-1.html
  3. Scalable IO in Java, Doug Lea, State University of New York at Oswego
  4. SelectionKey (Java Platform SE 7 )
  5. Selector (Java Platform SE 7 )
  6. SelectableChannel (Java Platform SE 7 )
  7. Java NIO Selector By Jakob Jenkov
  8. http://jeewanthad.blogspot.kr/2013/03/reacter-pattern-explained-part-2.html
  9. http://jeewanthad.blogspot.kr/2013/03/reacter-pattern-explained-part-3.html


2013년 10월 24일 목요일

Vincent by Don McLean







Stary stary night 
Paint your palette 
blue and gray 
Look out on a summer's day 
With eyes that know 
the darkness in my soul 
Shadows on the hills 
Sketch the trees and the daffodils 
Catch the breeze 
and the winter chills In colors 
on the snowy linen land 

Now I understand 
What you tried to say to me 
And how you suffered 
for your sanity 
And how you tried 
to set them free 
They would not listen 
they did not know how 
Perhaps they listen now

Stary stary night 
Flaming flowers that brightly blaze 
Swirling clouds in violet haze 
Reflect in vincent's eyes of China blue 
Colors changing hue 
Morning field of amber grain 
Weathered faces lined in pain 
are soothed Beneath artist's loving hand

Now I understand 
What you tried to say to me 
And how you suffered 
for your sanity 
And how you tried 
to set them free 
They would not listen 
they did not know how 
Perhaps they listen now 

For they could not love you
but still 
your love was true
and when no hope was left 
in sight on that 
starry starry night.
You took your life
as lovers often do;
But I could have told you
Vincent
this world was never meant for one
as beautiful as you.

Starry starry night
portraits hung in empty halls
frameless heads on nameless walls
with eyes that watch the world and can't forget.
Like the stranger that you've met
the ragged men in ragged clothes
the silver thorn of bloddy rose
lie crushed and broken on the virgin snow.

now I think I know 
what you tried to say to me
how you suffered for your sanity
how you tried to set them free.
They would not listen
they're not list'ning still
perhaps they never will.



별이 총총한 밤 
파랑, 회색으로 팔레트를 
물들이고 
여름날, 
내 영혼의 어두운 면을 
궤뚫는 눈으로 밖을 바라봐요 
언덕에 드리운 그림자, 
나무와 수선화를 스케치하고 
눈처럼 하얀 리넨 캔버스에 
미풍과 겨울의 싸늘함을 
색깔로 그려내요 

당신이 뭘 말하려 했는지 
난 이제 알 것 같아요 
온전한 정신은 찾으려 
당신이 얼마나 
고통스러워 했는지, 
사람들을 자유롭게 하려고 
얼마나 노력했는지. 사람들은 
들으려 하지 않았죠 어떻게 듣는지도 몰랐죠 
아마도 지금은 귀기울일 거에요

별이 총총한 밤 
붉은 빛깔로 빛나는 화려한 꽃들
보랏빛 안개 속의 소용돌이치는 구름들
빈센트의 청자빛 눈 속에 비쳐지네요
색조를 바꾼 색들 
아침 들판의 호박색 곡물들
고통에 주름진 풍화된 얼굴들
화가의 사랑스러운 손길 아래 진정이 되네요

당신이 뭘 말하려 했는지 
난 이제 알 것 같아요 
온전한 정신은 찾으려 
당신이 얼마나 
고통스러워 했는지, 
사람들을 자유롭게 하려고 
얼마나 노력했는지. 사람들은 
들으려 하지 않았죠 어떻게 듣는지도 몰랐죠 
아마도 지금은 귀기울일 거에요 

그들은 당신을 사랑할 수 
없었기에, 하지만 
당신의 사랑은 여전히 진실했죠 
그리고 아무런 희망도 
남지 않은 별이 총총한 밤 
연인들이 그러듯이 
당신도 목숨을 끊어버렸죠 
하지만 난 당신께 이렇게 
말했어야 했어요 
이 세계는 당신과 같이 아름다운 사람에게는 
어울리지 않는 것이었답니다


별이 총총한 밤 
빈 벽에 걸려 있는 초상화들
이름 없는 벽의 액자 없이 걸린 이들
세상을 바라보고 있는 눈들 그리고 있을 수 없는
당신이 만났던 이방인들
누더기 옷을 입은 남루한 남자들
피투성이 장미의 은빛 가시들
순백의 눈 위에 누워 부서지고 짓밟히네요

이젠 알 것 같아요 
당신이 뭘 말하려 했는지 
온전한 정신을 갈구하며 
얼마나 괴로워 했고 
사람들은 자유롭게 하려고 
얼마나 노력했는지 
사람들은 들으려 하지 않았죠 
지금도 귀기울지 않고, 
어쩌면 앞으로도 그럴테죠.

Reality by richard sanderson ... la boum






Met you by surprise
I didn't realize
That my life would change forever
Saw you standing there
I didn't know i'd care
There was something special in the air

Dreams are my reality
The only kind of real fantasy
Illusions are a common thing
I try to live in dreams
It seems as if it's meant to be

Dreams are my reality
A different kind of reality
I dream of loving in the night
And loving seems all right
Although it's only fantasy

If you do exist honey don't resist
Show me your new way of loving
Tell me that it's true,
Show me what to do
I feel something special about you

Dreams are my reality
The only kind of reality
Maybe my foolishness has passed
And maybe now at last
I see how the real thing can be

Dreams are my reality
A wondrous world where I'd like to be
I dream of holding you all night
And holding you seems right
Perhaps that's my reality

Met you by surprise
I didn't realize that
My life would change forever
Tell me that it's true
Feelings that are new
I feel something special about you

Dreams are my reality
A wondrous world where I like to be
Illusions are a common thing
I try to live in dreams
Although it's only fantasy

Dreams are my reality
I like to dream of you close to me
I dream of loving in the night
And loving you seems right
Perhaps that's my reality  
우연히 당신을 만난것으로
내 인생이 영원토록 바뀔줄은
미처 몰랐어요.
거기 서 있는 당신을 보고서도
내가 당신을 좋아하게 될거란걸 몰랐었죠.
그때 거긴, 뭔가 특별한게 있었던거죠.

꿈이란 것 역시 나에겐 현실과도 같아요.
진짜 환상이라고도 할 수 있는 유일한 것이죠.
환상이란 것들은 흔한 것이지만,
난 그래도 꿈속에서 살려고 노력한답니다.
마치 그것이 운명인 것만 같아요.

꿈이란 것 역시 현실과도 같죠.
종류가 다른 현실일 뿐인거에요.
밤이되면 난 당신을 사랑하는 꿈을 꾸죠.
비록, 환상처럼 보일 뿐이지만,
그 사랑의 느낌은 정말 좋죠.

당신이 정말 존재하는거라면, 망설이지 마요.
그대의 새로운 사랑법을 내게 보여주세요.
당신의 사랑이 진짜라고 말해주세요.
내가 어떻게 해야 하는지, 알려주세요.
당신에게선, 뭔가 특별한 걸 느낄 수 있어요.

꿈이란 것 역시 현실과도 같아요.
단 하나밖에 없는 현실일 수도 있죠.
그동안의 내 어리석었던 시절은 끝났나봐요.
그리고 마침내, 이제서야,
진짜라는 것이 어떤 것인지 알 것만 같아요.

꿈이란 것 역시 현실과도 같죠.
내가 바랬던 그 놀라운 세상인거죠.
밤이면 당신을 꼭 안고 있는 꿈을 꿔요.
당신은 안고 있는 것이, 아무런 문제가 없는것
만 같아요. 아마도 이게 현실인 거겠죠.

우연처럼 당신을 만나고도,
난 내 삶이 영원토록 바뀌어질꺼라는걸
미처 알지 못했었죠.
이 모든게 사실이라고 말해주세요.
이런 느낌들도 새로운 것이라구요.
당신에게선, 뭔가 특별한 것이 느껴져요.

꿈이 현실로 온거에요.
내가 바랬던 그 놀라운 세상에 나 서 있어요.
환상이란 것이 흔한 것이라고 해도,
그것이 단지 정말 환상일 뿐이라 해도,
난 꿈속에서 살려고 노력한답니다.

꿈이 현실로 온거에요.
난, 당신이 내 가까이에 있는 꿈을 꿔요.
밤이면 당신을 사랑하는 꿈을 꾼답니다.
그리고 그 사랑엔 아무 문제가 없어 보여요.
아마도, 그것이 정말 내 현실일지도 몰라요.


2013년 10월 16일 수요일

[English]......have a problem

by steve jobs

  - If a user is having a problem, it's our problem
    (사용자가 문제를 겪고 있다면, 그건 우리 문제인거야)
  - have a problem
    + '문제가 있다'라는 표현으로 문제가 되는 대상을 얘기 할 때 주로 뒤에 with를 붙여서 말함
  - 내 세탁기에 문제가 있어
    + I have a problem with my washing machine.
    + My washing machine has a problem.
    + There is a problem with my washing machine.


dialog

A: Bill, are we having problems with our network? 
B: I think our whole network is down for upgrades. It should be back up in an hour. 
A: That's too late. I need to review documents from the company network share. 
B: I have a copy of that on my computer. 

  - review documents (문서를 검토하다)
  - have a copy (복사본이 있다) 


The vicious cycle(악순환)

  - You stay up late because you didn't do anything productive all day
    (하루종일 생산적인거라곤 하지 않아 늦게까지 달림)
  - You don't do anything all day because you stayed up too late. (and you goofed around a little)
    (넘 늦게까지 달려서 하루종일 뭘 못함(조금씩 시간 허비...))
  - You stay up even later to make up for it
    (만회하려 좀 더 늦게까지 달림)
  - You get even less done
    (되는 일은 더 없음)
  - You stay up even later
    (더 늦게까지 달림)
  - Pull all-nighter
    (밤샘)
  - Crash
    (혼절)


*vicious cycle : 악순환
*stay up late : 늦게까지 (뭐하며) 안자다. / stay up all night (밤새다)
*goof around : 시간을 허비하다
*make up for : 만회하다
*all-nighter : 밤샘공부 (슬랭) / pull an all-nighter (밤새워 공부하다)
*crash : 이상한데서 뻗어자다

Groovy 시작하기 - A dynamic language for the Java platform



Groovy...

  • is an agile and dynamic language for the Java Virtual Machine
  • builds upon the strengths of Java but has additional power features inspired by languages like Python, Ruby and Smalltalk
  • makes modern programming features available to Java developers with almost-zero learning curve
  • provides the ability to statically type check and statically compile your code for robustness and performance
  • supports Domain-Specific Languages and other compact syntax so your code becomes easy to read and maintain
  • makes writing shell and build scripts easy with its powerful processing primitives, OO abilities and an Ant DSL
  • increases developer productivity by reducing scaffolding code when developing web, GUI, database or console applications
  • simplifies testing by supporting unit testing and mocking out-of-the-box
  • seamlessly integrates with all existing Java classes and libraries
  • compiles straight to Java bytecode so you can use it anywhere you can use Java

Installing Groovy

These instructions describe how to install a binary distribution of Groovy.
  • first, Download a binary distribution of Groovy and unpack it into some file on your local file system
  • set your GROOVY_HOME environment variable to the directory you unpacked the distribution
  • add GROOVY_HOME/bin to your PATH environment variable
  • set your JAVA_HOME environment variable to point to your JDK. On OS X this is /Library/Java/Home, on other unixes its often /usr/java etc. If you've already installed tools like Ant or Maven you've probably already done this step.
You should now have Groovy installed properly. You can test this by typing the following in a command shell:
groovysh
Which should create an interactive groovy shell where you can type Groovy statements. Or to run the Swing interactive console type:
groovyConsole
To run a specific Groovy script type:
groovy SomeScript.groovy