Tag Archives: java

Apache MINA 2.0 에코서버 만들어 보기

사용자 삽입 이미지

제작년인가 JCO 컨퍼런스에서 이희승님의 발표 이후에 잊을 수 없던 프로젝트가 있었습니다.

바로 MINA(A Multi-purpose Infrastructure for Network Applications)인데요.

간단하게 말하면 자바의 네트워크 애플리케이션을 위한 프레임워크입니다.

필터를 사용한 뛰어난 확장성과 프로토콜 코덱과 비즈니스 로직을 분리하여 유지보수와 재사용성을 높인것이 특징입니다.

더군다나 커미터가 이희승님이라는 것이 중요한 점입니다. 한글로 질문해도 답변해 주시겠죠? -_-a

간단하게 예제 프로그램을 따라 만들어 보았습니다. [이곳]을 참고하였습니다.

현재 MINA2가 M3까지 나왔더군요. 예제를 위해서는 MINA코어뿐만 아니라 SLF4JLog4J가 필요합니다.

SLF4J의 경우에는 slf4j-api.jar파일과 slf4j-logj12.jar가 필요합니다. 13버젼용도 있지만 아직 알파버젼이니 12로 하기로 하였습니다.

그리고 log4j 1.2버젼의 jar로 세팅합니다.

MinaTimeServer
[code]import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
 
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
 
public class MinaTimeServer
{
    private static final int PORT = 9123;
 
    public static void main(String[] args) throws IOException
    {
        IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast( “logger”, new LoggingFilter() );
        acceptor.getFilterChain().addLast(“codec”, new ProtocolCodecFilter(
                                            new TextLineCodecFactory(Charset.forName(“UTF-8”))));
 
        acceptor.setHandler(new TimeServerHandler());
 
        acceptor.getSessionConfig().setReadBufferSize(2048);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
 
        acceptor.bind(new InetSocketAddress(PORT));
    }
}[/code]
Non-Blocking IO로 만듭니다. FilterChain이라는것에 필터를 추가합니다.

addLast라는 것을 보니 내가 원하는 순서대로 필터를 등록할 수 있는 모양입니다.

위와 같이 로깅을 하거나 인코딩 코덱을 만들어 사용할 수 있습니다.

핸들러를 추가하고 버퍼와 유휴시간을 정의한 후에 PORT를 설정하여 Bind합니다.

TimeServerHandler
[code]import java.util.Date;
 
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
 
public class TimeServerHandler extends IoHandlerAdapter
{

    @Override
    public void exceptionCaught(IoSession session, Throwable cause)
        throws Exception
    {
        cause.printStackTrace();
    }

    @Override
    public void messageReceived(IoSession session, Object message)
        throws Exception
    {
        String str = message.toString();
 
        if(str.trim().equalsIgnoreCase(“quit”))
        {
            session.close(true);
            return;
        }
 
        Date date = new Date();
        session.write(date.toString() + “\r\n”);
        System.out.println(“Message written…”);
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status)
        throws Exception
    {
        System.out.println(“IDLE ” + session.getIdleCount(status));
    }
}[/code]
이녀석이 실제로 통신에 사용되는 핸들러입니다. 메시지를 받을때 마다 messageReceived이 호출됩니다.

쓰레드 방식의 모델이라 보기 힘든 구조네요, 콜백형식으로 작동하는 것을 알 수 있습니다.

다음에는 객체를 Serial로 주고 받을 수 있는지 해봐야겠습니다.

sessionIdle은 Main에서 정의한 유휴시간마다 호출되는 녀석입니다. 10초로 설정해 두었으니 10초동안 유휴상태로 있다면 그때마다 이 메서드가 호출됩니다.

getIdleCount를 이용해 카운트도 알 수 있군요. 서버를 완성했으니 실행해 볼까요.

사용자 삽입 이미지


quit를 입력하면 종료되고 그 이외의 문자를 입력하면 시간이 출력되는 것을 알 수 있습니다.

[Flex + Spring] Annotation 기반 Flex BlazeDS 서비스 개발하기

Flex 개발을 하다보면 서버와의 통신을 통해 다양한 방법을 사용할수 있다는 장점을 쉽게 알 수 있습니다.

하지만 안타까운게 Remote Object를 사용하기 위해서는 얼마인지 산출하기도 힘든 비싼 FDS (LCDS)를 구매해야 한다는 장벽이 있었죠.

이 장벽에 막혀 안정성이 확보되지 못한 일부 오픈 소스제품군을 사용하거나 다른 방식의 통신을 사용하여야 했습니다.

하지만 LCDS의 빠른 속도를 보장받을수는 없었죠. 현재 LCDS의 Remote Object 통신 프로토콜은 AMF3 (Action Message Format 3)까지 나왔습니다.

하지만 Adobe에서 BlazeDS라는 이름의 LCDS 무료버젼을 내놓았습니다. 또한 오픈소스로 개발 진행중입니다.

또한 Jeff Vroom 이라는 멋진 분이 Spring ↔ BlazeDS 간 통신할 수 있는 Factory를 개발하였습니다.

관련 정보는 다음을 참고하세요

[ 어도비 기술문서 ]
[ Sewon님 블로그 ]
[ 머드초보님 블로그 ]

하지만 위의 기술 내용들은 Spring Framework를 이용하여 어노테이션 기반으로 개발중이라면 여간 성가시게 만드는 부분이 많지 않은가 싶습니다.

위의 내용도 매우 간단해 지고 쉬워졌지만 BlazeDS도 어노테이션을 이용해 연동할 수 있으면 얼마나 좋을까 생각하게 만들었습니다.

그러던중에 좋은 자료를 발견하였습니다.

1. Spring에서 @RemotingDestination 어노테이션을 사용하기 위해 다음의 라이브러리를 다운받습니다. 여기에는 Jeff Vroom씨가 만든 Spring Factory도 포함하고 있습니다.

[ 다운받으러 가기 ]

2. remotingdestination-annotation.zip 파일을 개발중인 프로젝트의 WEB-INF/lib 디렉토리에 넣어줍니다.

3. BlazeDS의 설정파일인 services-config.xml 에 SpringAutowiringBootStrapService를 등록해 줍니다.
[code]<?xml version=”1.0″ encoding=”UTF-8″?>
<services-config>
    <services>
        <service-include file-path=”remoting-config.xml” />
        <service-include file-path=”proxy-config.xml” />
        <service-include file-path=”messaging-config.xml” />
            <service id=”spring-autowiring-bootstrap-service”
                class=”flex.contrib.services.SpringAutowiringBootstrapService”/>
        <default-channels>
            <channel ref=”my-amf”/>
        </default-channels>
    </services>


….

    <factories>
        <factory id=”spring” class=”flex.contrib.factories.flex.SpringFactory” />
    </factories>
</services-config>[/code]
SpringAutowiringBootStrapService은 BlazeDS 로딩시에 모든 @RemotingDestination 어노테이션이 포함된 클래스를 등록합니다. 이것은 Spring Factory를 통해 동적으로 Spring의 Bean을 불러다 사용할 수 있음을 뜻합니다. 밑에 추가한 factory의 경우 id는 꼭 spring이어야 합니다.

4. 마지막으로 당신의 서비스 클래스에 @RemotingDestination 어노테이션을 붙여줍니다.
[code]package flex.contrib.samples.mortgage;


import org.springframework.beans.factory.annotation.Autowired;
import flex.contrib.stereotypes.RemotingDestination;


@RemotingDestination(destination=”mortgageService”)
public class Mortgage {
    @Autowired RateFinder rateFinder;
    public void setRateFinder(RateFinder rateFinder) {
        this.rateFinder = rateFinder;
    }


    public double calculate(double amount) {
        int term = 360; // 30 years
        double rate = rateFinder.findRate();
        return (amount*(rate/12)) / (1 – 1 /Math.pow((1 + rate/12), term));
    }
}[/code]

5. 물론 Spring에서 component-scan과 annotation-config를 설정해 주는걸 잊지 말자고요.
[code]<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns=”http://www.springframework.org/schema/beans
       xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
       xmlns:context=”http://www.springframework.org/schema/context
       xsi:schemaLocation=”http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-2.5.xsd“>


    <context:annotation-config/>
    <context:component-scan base-package=”flex.contrib.samples”/>
</beans>[/code]

6. Flex 에서는 다양한 방법의 Remote Object 통신방법으로 서버의 서비스에 직접 접근할 수 있게 됩니다.
[code]var amfChannel:AMFChannel = new AMFChannel(“my-amf”, “http://localhost/messagebroker/amf“);
var channelSet:ChannelSet = new ChannelSet();
channelSet.addChannel(amfChannel);
   
var remoteObject:RemoteObject = new RemoteObject();
remoteObject.channelSet = channelSet;
remoteObject.destination = “mortgageService”;
remoteObject.addEventListener(ResultEvent.RESULT, resultHandler);
remoteObject.addEventListener(FaultEvent.FAULT, faultHandler);
remoteObject.calculate(5);[/code]

+. @RemotingDestination에서 지정한 destination이 Flex에서 지정하는 destination이 됩니다. 주의할점이 하나 있는데 web.xml의 contextConfigLocation 설정에서 정의한 컨텍스트의 Bean만 가져올 수 있습니다.

참고자료 : http://marceloverdijk.blogspot.com/2008/01/code-by-convention-with-flex-and-spring.html