오랜만에 포스팅을 하네요 :)

오늘은 Java를 가지고 application 프로토콜을 만드는 방법에 대해서 포스팅을 하려고 합니다.
"application 프로토콜이면, 그냥 header 와 data 구조를 정의하고 포트만 결정하면 되지 않나?" 라고 생각하실 겁니다.

이 시간에 말하고자 하는건 이런 프로토콜하고는 조금 다릅니다.
URL 에 사용되는 프로토콜(스키마)를 말하는 것입니다.

http, https, ftp 등등 이런 것들 말이죠.

cube://Hong-gildong/profile
이런식의 URL 을 만들어 보도록 하겠습니다.

Java 에서는 내부적으로 이런것이 가능하도록 지원해 주고 있습니다.
URL Class 를 보면, static 으로 URL.setURLStreamHandlerFactory(URLStreamHandlerFactory arg) 라는 메소드를 제공합니다.

안에 들어가는 파라미터인 URLStreamHandlerFactory 는 interface 로서 아래와 같습니다.


package java.net;
/**
 * This interface defines a factory for <code>URL</code> stream
 * protocol handlers.
 * <p>
 * It is used by the <code>URL</code> class to create a
 * <code>URLStreamHandler</code> for a specific protocol.
 *
 * @author  Arthur van Hoff
 * @version %I%, %G%
 * @see     java.net.URL
 * @see     java.net.URLStreamHandler
 * @since   JDK1.0
 */
public interface URLStreamHandlerFactory {
    /**
     * Creates a new <code>URLStreamHandler</code> instance with the specified
     * protocol.
     *
     * @param   protocol   the protocol ("<code>ftp</code>",
     *                     "<code>http</code>", "<code>nntp</code>", etc.).
     * @return  a <code>URLStreamHandler</code> for the specific protocol.
     * @see     java.net.URLStreamHandler
     */
    URLStreamHandler createURLStreamHandler(String protocol);
} 



그리고 리턴해야 하는 URLStreamHandler 는 abstract 클래스 입니다.

먼저 URLStreamHandlerFactory 를 먼저 봅시다.
이름을 보면 어떤 패턴인지 감이 오시나요?
바로 '추상 팩토리 패턴'입니다.

URL 은 내부적으로 factory 라는 녀석을 가지고 있습니다.
이 녀석이 하는 일은 http, https, ftp 와 같은 protocol 을 보고, 어떤 URLStreamHandler 를 만들지 결정하는 것이지요.
바로 Factory 입니다.


cube 를 URL 이 인식하도록 URLStreamHandler 를 아래와 같이 구현해 봅시다.


public class CubeURLStreamHandlerFactory implements URLStreamHandlerFactory {

	@Override
	public URLStreamHandler createURLStreamHandler(String protocol) {
		try {
			// 내가만든 cube 프로토콜이면 CubeURLStreamHandler 를 리턴
			if(protocol.equals("cube") == true) {
				return new CubeURLStreamHandler();
			}
			
			// 아니라면 원래 코드 수행
			String className = "sun.net.www.protocol." + protocol + ".Handler";
			Class<?> type = Class.forName(className);
			if(type == null) { return null; }
			return (URLStreamHandler)type.newInstance();
		} catch(Exception e) {
			return null;
		}
	}
} 



코드를 보시면 아시겠지만, 하는 역할이라고는 protocol 이 cube 이면, 우리가 만든(만들) CubeURLStreamHandler 를 리턴할 것입니다.
만약 그게 아니고 http, https, ftp 와 같은 것들이면, 그에 맞는 URLStreamHandler 를 리턴하는 것이지요.

switch(protocol) { case "http": return new sun.net.www.protocol.http(); break; case "https": .......}
이런식으로 해도 되는데, 귀찮아서 조금 간결하게 짠 코드입니다.
오해 없으시기 바랍니다.


자, 그럼 CubeURLStreamHandler 가 구현해야 하는 추상클래스인 URLStreamHandler 에 대해서 살펴보죠.



생각보다 간단합니다. 저게 전부입니다. 별도로 이녀석이 상속받은게 없습니다. Object 바로 아래에 있는 최상위 클래스입니다.
가장 중요한 메소드는 openConnection() 입니다. URL 이 connect 를 할때 불리는 함수이지요.
그리고 cube://Hong-gildong/profile 과 같이 cube:// 다음에 사람 이름을 쓰고 싶다면, 어떤 메소드를 override 하면 될까요?
바로 parseURL(URL, String, int, int) 입니다.

이곳에서 URL 의 Hong-gildong 부분을 가져와서 member 로 저장하고 있으면 되겠죠.
(url 의 getAuthority() 메소드를 호출하면 손쉽게 가져올 수 있습니다.)

이제 다 끝났습니다.
openConnection() 호출시, URLConnection 을 상속은 나만의 URLConnection 을 리턴하면 됩니다.


URLConnection 역시 abstract 클래스입니다.
안에 있는 중요 메소드들을 보면, connect, getOutputStream, getInputStream 정도가 되겠습니다.
getOutputStream, getInputStream 의 리턴값으로 사용할 나만의 Stream 을 만들수도 있습니다.

connect() 호출시, c:\abc.txt 라는 파일을 만들고,
getOutputStream(), getInputStream() 에서 각각 FileStream 을 리턴해 주면 어떻게 될까요?

아마 접속시 전송되는 데이타가 c:\abc.txt 에 써 지겠죠.
물론 http header 도 포함해서 말입니다.

이제 Application 을 만들고, url 을 입력받는 창을 하나 만듭니다.
그리고 이곳에 사용자가 cube://Hong-gildong/profile 이라고 치면, c:\abc.txt 에 request 가 작성되도록 할 수 있습니다.
물론 http://naver.com 이라고 치면, 정상적으로 naver.com 에 연결도 되고요.

즉, 기존 protocol 은 손대지 않고 그대로 살려두고, 새로 protocol 을 추가한 것입니다.
어떤가요?

나머지 부분은 쉽게 구현하실 수 있을거라고 믿습니다. :)
참,, 지금 설명드린 부분은 Client 쪽인데, Server 부분도 마찬가지로 '추상 팩토리 패턴'으로 되어 있습니다.
조금만 응용하시면 나만의 프로토콜을 인식하는 Server 도 만드실 수 있습니다.


오늘 포스팅은 이것으로 마치도록 하겠습니다.
퇴근해야 겠네요 ㅎ;