2015년 9월 10일 목요일

[Java] Poison Pill Shutdown

Java Concurrency Framework을 사용해서 Producer/Consumer 형태의 코드를 종종 작성하게 됩니다. 대표적으로 사용하는 것이 바로 BlockingQueue 입니다. BlockingQueue는 thread-safety하게 작성되었기 때문에, thread 처리에 대한 고민을 덜어줍니다.

BlockingQueue에 객체를 넣는 역할을 Producer(생산자)라고 하고,
BlockingQueue에서 객체를 빼내와서 어떤 작업을 하는 것을 Consumer(소비자)라고 합니다.
전형적인 Producer/Consumer 코드는 아래와 같은 모습을 합니다.

public class Producer {
    BlockingQueue<Object> queue = new LinkedBlockingQueue<>()

    public void produce(Object o) {
        queue.add(o);
    }
}

public class Consumer implements Runnable {
    private BlockingQueue<Object> queue;
    public Consumer(BlockingQueue<Object> queue) {
        this.queue = queue;
    }

    public void run() {
       try {
            while(true) {
                Object o = queue.take()
                // do something
            }
       }
       catch() {
            // do somehting
       }
    }
}
여기서 문제가 될 수 있는 부분은 바로 queue.take() 입니다.
take() 메쏘드는 queue가 비어있는 경우 block이 됩니다.
queue에 무엇인가 있지 않으면 영원히 종료되지 않을 수 있다는 뜻이기도 합니다.

BlockingQueue에는 poll이라는 메쏘드도 있어서, queue가 비어 있는지를 확인하고 비어 있으면, 일정 시간 동안만 blocking 하고 있다가, 다음 실행을 할 수도 있습니다.
 그런데 queue의 값을 계속 처리해야하는 Consumer의 경우는
- queue가 비어 있는지를 확인, 
- 비어 있으면 정해지 시간동안 기다림
- 아래 문장을 실행
- 다시 queue에 값이 있는지 확인 
이런 일을 반복해야 합니다.

이런 경우를 busy-wait라고 합니다. queue의 값이 오기를 기다리기는 하는데 계속 분주하게 움직이고 있는 것이지요.
그래서 저 같은 경우는 take() 메쏘드를 선호합니다. 

그러면 take() 메쏘드를 사용하면서도 영원히 종료되지 않는 문제를 해결하면서 graceful shutdown을 하려면 어떻게 해야 할까요?
이 때 사용하는 것이 Poison Pill(독약) 입니다. 독약을 먹여서 thread를 죽이는 것이지요.

Poison Pill은 무한 루프를 벗어나기 위한 조건이 되는 특별한 객체를 의미합니다. 
시스템이 shutdown 한다는 신호를 받으면, Poison Pill 객체를 queue에 넣고, Consumer는 Poison Pill 객체를 받으면 무한 루프를 벗어나는 것입니다.
public class Consumer implements Runnable {
    private BlockingQueue<&gt queue;
    public Consumer(BlockingQueue<Object> queue) {
        this.queue = queue;
    }

    public void run() {
       try {
            while(true) {
                Object o = queue.take()
                if(o instanceof PoisonPill) {
                     break;
                }
                // do something
            }
       }
       catch() {
            // do somehting
       }
    }

    private class PoisonPill {
    }
}
클래스 이름은 원하는 것으로 아무거나 만들어도 됩니다.

 사실 별거 아니지요. 그런데 저는 이런 아이디어 떠오르지 않아서 꽤 고민했었네요. 
스프링 프레임워크를 사용한다면, Consumer를 DisposableBean를 구현하게 하고
public void destroy() {
    queue.add(new PoisonPill());
}
시스템이 종료할 때, 종료 신호를 받아서 처리하면 될 것 같습니다.

[Mac 초보] Mac에서 FaceTime으로 전화가 안될 때

Yosemite 버전부터 iOS와 연결이 많이 강화 되었지요.
그래서 맥으로 작업 중에 iMessage 뿐 아니라, 일반 SMS도 맥으로 확인할 수 있고, FaceTime 오디오뿐 아니라 아이폰으로 걸려오는 일반 전화도 걸고 받을 수 있습니다.

그런데 언제부턴가 FaceTime으로 일반 전화를 하려고하면 다음과 같은 메시지가 나옵니다.

"iPhone이 반드시 같은 iCloud 및 FaceTime 계정을 사용해야 합니다"

이 메시지를 보고 제 아이폰과 맥의 iClound와 FaceTime을 확인을 해 보았습니다. 그러나 이미 동일한 계정으로 iCloud와 FaceTime은 로그인이 되어 있었습니다.

구글링을 해보니 OS X 버전이 업데이트 되면서 가끔 이런 현상이 발생한다고 하네요.

해결 방법은

  1. 맥의 FaceTime 실행
  2. FaceTime 환경 설정
  3. FaceTime 로그아웃
  4. FaceTime 재 로그인 (여기까지만 해도 해결이 되는 경우가 많다고 합니다. 그런데 제 경우는 아니더군요.)
  5. 아이폰의 설정 -> FaceTime
  6. Apple ID 터치
  7. 로그아웃
  8. 재 로그인
  9. 맥 화면에서 아이폰의 전화 번호를 FaceTime에 추가할지를 물어봅니다.
결론적으로 제 경우는 아이폰과 맥이 모두 동일한 iClound, FaceTime 계정을 사용하고 있었지만, 아이폰의 전화번호가 삭제된 상태였습니다.
맥과 아이폰에서 FaceTime을 재로그인 하니까, 아이폰의 전화번호를 추가할지를 물어보았습니다.

아래 그림과 같이 맥과 아이폰에 "전화번호"가 보여야 정상적으로 SMS와 일반 전화를 사용할 수 있습니다.

맥의 FaceTime 설정

아이폰의  FaceTime 설정

2015년 5월 21일 목요일

Java Dynamic Proxy vs CGLib Proxy vs AspectJ Weaving #4

3. AspectJ Weaving
AspectJ는 AOP(Aspect Oriented Programming)을 가능케 해주는 Java 라이브러리 입니다.
실제로 현업에서  AspectJ를 많이 쓰고 있는지는 잘 모르겠지만, Spring AOP를 이해하는데 도움이 될 것 같습니다. 동작 원리는 비슷하니까요.
AspectJ는 사실 Proxy와는 다르지만, 기존 코드를 수정하지 않고 추가적인 기능을 만들 수 있다는 점에서는 거의 비슷하다고 볼 수 있습니다.

예제를 한번 보겠습니다. 다음과 같이 간단한 문자열을 출력하는 Target.test() 메쏘드가 있습니다.
package aspectj;

public class Target { 
 public void test() {
  System.out.println("Target.test()");
 }

}

위의 test() 메쏘드가 호출될 때마다, 또다른 문자열을 출력하고 싶을 때 다음과 같은 @Aspect를 추가할 수 있습니다.
package aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoggingAspect {
 
 @Before("execution (* aspectj.Target.test*(..))")
 public void advice(JoinPoint joinPoint) {
  System.out.printf("LoggingAspect.advice() called on %s %n", joinPoint);
 }
}

@Before 어노테이션은 target 메쏘드가 실행되기 전에 실행됩니다. execution 뒤에 있는 문자열의 의미는

* : 모든 access modifier (public, private, protected)
aspectj.Target.test*: aspectj.Target클래스 내의  이름이 test로 시작하는 모든 메쏘드
(..): 모든 매개변수에 대해서

위의 advice() 메쏘드는 aspectj.Target 클래스에서 이름이 test로 시작하는 모든 메쏘드 (public, private, proteced 상관없이)가 실행되기 전에 항상 실행됩니다.

아래 링크에서 소스를 받아서 메이븐을 실행하면 다음의 결과를 볼 수 있습니다.

> mvn clean test

Running aspectj.LogginAspectTest
LoggingAspect.advice() called on execution(void aspectj.Target.test()) 
Target.test()
LoggingAspect.advice() called on execution(void aspectj.Target.test2()) 
Target.test2()

AspectJ를 사용함에 있어 한가지 주의해야 할 것이 있는데, aspectj-maven-plugin이 compiler-maven-plugin과 호환되지 않는 부분이 있습니다. 이것 때문에 원인을 찾기에 고생을 좀 했네요. complianceLevel을 1.6으로 해주어야 정상적으로 동작합니다.

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<complianceLevel>1.6</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>



전체 소스 코드는 https://github.com/jinwooe/dynamic-proxy-cglib-aspectj-example 에서 받을 수 있습니다

참고 사이트
http://java.dzone.com/articles/power-proxies-java
http://denis-zhdanov.blogspot.kr/2009/08/weaving-with-aspectj.html

Java Dynamic Proxy vs CGLib Proxy vs AspectJ Weaving #3

2. CGLib Proxy
CGLib는 바이트 코드를 조작해서 Proxy를 만듭니다.
CGLib를 사용해서 Proxy를 만들면, interface를 정의하고 구현할 필요가 없고, 다른 클래스를 상속할 수 도 있습니다.
예를 들어, Hibernate의 Entity는 POJO인데, Java Dynamic Proxy로는 lazy loading을 구현할 수 없지만, CGLib로는 가능합니다.

Java Dynamic Proxy에서 java.lang.reflect.Proxy가 했던 유사한 일은 CGLib에서는 net.sf.cglib.proxy.Enhancer 가 담당합니다.
그리고 java.lang.reflect.InvocationHander가 했던 일은 net.sf.cglib.proxy.Callback이 합니다. 그런데 Callback을 직접 사용하지 않고, 다음의 Callback sub interface 중에 하나를 선택하여 합니다.

Dispatcher: 디스패치 callback
FixedValue: proxied 메쏘드가 리턴하는 값을 그대로 리턴
LazyLoader: lazy loading callback
MethodInterceptor: "around advice" 기능을 제공하는 범용 callback
NoOp: 부모(super) 메쏘드를 그대로 호출

예제를 한번 보겠습니다.
package cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class HashCodeAlwaysZeroMethodInterceptor implements MethodInterceptor {

 public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

  if ("hashCode".equals(method.getName())) {

   return 0;
  }

  return methodProxy.invokeSuper(object, args);
 }
}

위 예제에서는 MethodInterceptor를 Enhancer callback으로 사용했습니다. 모든 오브젝트에 대해서 hashCode()를 호출했을 때 0을 리턴합니다.
MethodInterceptor는 InvocationHandler와 매유 유사하게 생겼습니다.

Proxy를 생성하는 방법은 다음과 같습니다.
proxy = Enhancer.create(Object.class, new HashCodeAlwaysZeroMethodInterceptor());

Enhancel.create() 메쏘드의 매개변수는
target object의 타입
callback 객체

뿐입니다. interface 구현과 관련된 것이 없습니다.
Proxy 객체를 만들 때, target object도 같이 생성됩니다. 때문에 target object를 생성하고 메모리에 저장할 필요가 없습니다.

전체 소스 코드는 https://github.com/jinwooe/dynamic-proxy-cglib-aspectj-example 에서 받을 수 있습니다

참고 사이트
http://java.dzone.com/articles/power-proxies-java
http://denis-zhdanov.blogspot.kr/2009/08/weaving-with-aspectj.html

2015년 5월 20일 수요일

Java Dynamic Proxy vs CGLib Proxy vs AspectJ Weaving #2

이번 포스트에서는 Proxy 패턴을 구현하는 방법에 대해서 얘기해 보겠습니다.

Proxy 패턴을 구현할 때, 개발자가 직접 패턴을 구현할 수도 있겠지만 이미 만들어져 있는 도구를 잘 사용하는 것도 좋을 것 같습니다.

흔희 많이 사용하는 방법은 Java API에서 제공하는 dynamic proxy를 이용하는 방법과, cglib를 사용해서 바이트 코드를 핸들링해서 만드는 방법이 있습니다.
그리고 위의 두가지 방법과는 조금 다르긴 하지만, 기존 코드를 수정하지 않고 기능을 추가하는 방법인 AOP의 구현체인 AspectJ 라이브러리를 사용하는 방법이 있습니다.

1. Java Dynamic Proxy
Java Dynamic Proxy는 런타임시에 interface를 구현하는 java.lang.reflect.Proxy 클래스의 서브클래스 객체를 생성합니다.
Java Dynamic Proxy는 interface를 구현하는 것이기 때문에, Proxy를 사용하려면 interface 정의를 반드시 해야합니다. 즉 Client에서 사용하려는 inteface를 정의해야 합니다.

Proxy 객체가 어떤일을 할 것인지를 결정하는 것은 java.lang.reflect.InvocationHandler의 구현체에서 합니다.
InvocationHandler에는 한 개의 메쏘드가 정의되어 있습니다.

public Object invoke(Object proxy, Method method, Object[] args)

proxy: 메쏘드 호출이 일어났던 proxy 인스턴스
method: 메쏘드 호출 당시의 interface 정의에 있는 java.lang.reflect.Method의 인스턴스
args: 위 method의 매개변수들

예제를 한번 살펴 보겠습니다.
package dynamic_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;

public class NoOpAddInvocationHandler implements InvocationHandler {
 private final List<E> proxied;
 
 public NoOpAddInvocationHandler(List<E> proxied) {
  this.proxied = proxied;
 }
 
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  if(method.getName().startsWith("add")) {
   return false;
  }
  
  return method.invoke(proxied, args);
 }

}

위의 예제는 java.util.List에 대한 Proxy를 생성하는 코드입니다. List가 interface라는 것을 주의하시기 바랍니다. List의 메쏘드 중에서 메쏘드 이름이 "add"로 시작하는 메쏘드가 호출되면 무시합니다. 예를 들어, List의 add()나 addAll()을 호출하게 되면, 아무런 일을 하지 않게 됩니다. 그 외의 메쏘드 호출은 method.invoke()를 통해서 target object (여기서는 proxied)의 메쏘드를 그대로 호출합니다.
다음은 테스트 코드입니다.
package dynamic_proxy;

import static org.junit.Assert.assertTrue;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

public class NoOpAddInvocationHandlerTest {
 private List<Object> list;
 
 private List<Object> proxy;
 
 @Before
 public void setUp() {
  list = new ArrayList<Object>(0);
 }
 
 @SuppressWarnings("unchecked")
 @Test
 public void testInvoke() {
  
  proxy = (List<Object>) Proxy.newProxyInstance(NoOpAddInvocationHandlerTest.class.getClassLoader(),
    new Class[] {List.class}, new NoOpAddInvocationHandler<Object>(list));
  
  assertTrue(proxy instanceof Proxy);
 }
 
 public void testAdd() {
  proxy.add(new Object());
  
  assertTrue(list.isEmpty());
 }
}

위 테스트 코드에서는 List의 구현체인 ArrayList를 사용했습니다. NoOpAddInvocationHandler의 Proxy를 java.lang.reflect.Proxy를 통해서 생성합니다.
Proxy 객체를 생성할 때, 3개의 매개변수가 필요합니다.

  1. 클래스 로더
  2. 인터페이스 리스트
  3. InvocationHandler 구현체

클래스 로더는 테스트코드의 클래스로더와 동일한 것을 사용합니다.
인터페이스 리스트는 Proxy로 만들려는 모든 interface를 나열합니다. 여기서는  List 하나입니다.
InvocationHandler 구현체는 위에서 작성한 NoOpAddInvocationHandler 객체를 사용합니다.

위의 테스트를 실행하면, testAdd()에서 Object 객체를 추가하였음에도 List의 사이즈가 늘어나지 않았음을 알 수 있습니다.

전체 소스 코드는 https://github.com/jinwooe/dynamic-proxy-cglib-aspectj-example 에서 받을 수 있습니다

참고 사이트
http://java.dzone.com/articles/power-proxies-java
http://denis-zhdanov.blogspot.kr/2009/08/weaving-with-aspectj.html

Java Dynamic Proxy vs CGLib Proxy vs AspectJ Weaving #1

모든 것이 그렇지만 기술에 있어서도 기본이 가장 중요한 것 같습니다. 복잡한 기술이라도 기본 원리를 잘 이해하고 있다면 그것을 사용함에 있어서 많은 문제들을 잘 극복하게 해 줄 것입니다.

오늘은 GoF 디자인 패턴 중에서 Proxy 패턴에 대해 알아보고, Proxy 패턴을 구현하는 방법과 어떤 곳에서 활용되고 있는지를 알아보려고 합니다.

Proxy를 사전에 찾아 보면 "대리인" 이라는 뜻을 가지고 있습니다. Proxy 개념을 가장 쉽게 접할 수 있는 것이 Proxy Server가 아닐까 생각이 됩니다.
회사의 사내망은 사설망(Private Network)으로 구성되고 인터넷 망과 연결할 때 보안을 위해 Proxy Server를 두게됩니다. 사내망에서 인터넷으로 나가거나 반대로 인터넷망에서 사내망으로 접속할 때, 모든 통신은 Proxy Server를 거쳐서 이루어 지게 됩니다.
인터넷망에서 사내망을 바라볼 때 사내망 내부는 어떻게 구성되어 있는지 알지 못하고  Proxy Server하고만 통신을 주고 받게 됩니다. 연결을 받아들인 Proxy Server는 사내망안에 대상이 되는 사용자 컴퓨터를 찾아서 연결해 줍니다. 그래서 "대리인"이라는 뜻의 Proxy를 사용하고 있습니다.

프로그래밍에서도 Proxy는 비슷한 개념으로 사용됩니다. 실제 사용할 객체를 대신하여 Proxy 객체가 일을 담당합니다. 이렇게 함으로써 Proxy 객체가 실제 객체에 대한 접근 제어를 할 수 있고, 때로는 실제 객체가 하는 일 외에 추가적인 작업을 할 수 있습니다.

아래 그림에서 Client는 target object의 request() 메쏘드를 호출하지만, 실제는 target object를 감싸고 있는 Proxy 객체가 요청을 가로챈 후에 target object의 request() 메쏘드를 호출합니다.
Client 입장에서는 Proxy가 target object로 보이게 되는 것입니다.


그렇다면 Proxy 패턴은 어떤 경우에 사용이 될까요?

Hibernate나 iBatis/MyBatis를 사용해 본 적이 있다면, lazy loading이라는 말을 들어보았을 것입니다. lazy loading은 실제 데이터베이스에서 데이터를 가져오는 작업을 최대한 늦게 한다는 것입니다. 데이터를 늦게 가지고 온다면, 실제로 프로그램 실행도 늦어지게 됩니다. 그래서 Proxy 객체를 사용해서 마치 데이터베이스에서 데이터를 가져온 것처럼 Proxy 객체를 리턴하고, 실제로 프로그램 로직에서 데이터를 사용해야할 때, Proxy 객체가 target object를 사용해서 데이터베이스에서 데이터를 가져오게 합니다.

또는 Spring AOP(Aspect Oriented Programming) 처럼 비지니스 로직과 트랜잭션(transaction), 로깅(logging) 등 비지니스 로직은 아니지만 프로그램 실행에 있어 꼭 필요한 기능을 비지니스 로직과 분리하여 개발할 때 Proxy 패턴을 사용할 수 있습니다. 다시 말해 어떤 기능에 추가 기능을 넣고 싶은데, 기존 코드를 수정하지 않고 기능을 추가하고 싶을 때 Proxy 패턴을 사용할 수 있습니다.

다음 포스트에서는 Proxy 패턴을 구현하는 방법에 대해서 알아보겠습니다.

2015년 5월 15일 금요일

Distributed Cache vs Replicated Cache

Distributed Cache와 Replicated Cache를 쉽게 설명한 글

https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_intro.htm#COHDG318