IT_Programming/Dev Libs & Framework

[Spring JMS] Spring JMS 기본예제

JJun ™ 2014. 7. 15. 06:24

 


 출처: http://yunsunghan.tistory.com/125

        http://yunsunghan.tistory.com/126


 

 

JMS 모델중 Point-to-point 메시징 모델인 Queue 방식에 대한 예제를 만들어 본다.
물론 예제는 Spring 기반이다.

1. 메시지 서비스 구조

보내는이(Sender)  -- Message --> 큐(Queue)  --Message-->  받는이(Receiver)


보내는이와 받는이는 구현해야 한다. 물론 Spring의 편리한 Template 클래스를 이용해서 말이다.
큐에 쌓이거나 주고 받는것은 아파치의 ActiveMQ가 알아서 해준다.
구현하는것은 ActiveMQ에 잘 보내고, 잘 가져오는 것이다. 코딩하기전에 위의 구조를 먼저 이해한 후 코딩에 들어가면 쉽게 이해할수 있다.


2. 브로커 설치 (Apache의 ActiveMQ를 사용한다.)
    메시지 브로커는 ActiveMQ를 사용한다.
    - 다운 받아 원하는 디렉토리에 압축을 풀고 activemq.bat(Window일경우)를 실행한다.


    - 3개의 리스닝 프로토콜을 볼수 있다. (tcp,ssl,stomp)
    - 이제 해당 URL로 메시지를 브로킹 할수 있다.


 

3. ActiveMQ 와 연동하기 Bean 설정

순서적으로 한다면, 기본적인 XML 설정후 클래스 하나씩 생성후 XML과 매핑해야 하나,

한꺼번에 봐도 그리 헷깔리지 않는다. 그래서 그냥 쭉 내려 보자.

<!--  Autowired -->

    <context:component-scan base-package="net.max.msg" />
   
<!-- ActiveMQ factory -->
   
    <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616" />
    </bean>
: ActiveMQ와 Connection 정보를 가져오게 한다.

<!-- Destination - Queue Model -->

    <bean id="maxDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg index="0" value="net.max.msg.queue" />
    </bean>   
: destination을 설정한다.

<!-- JMS Template -->

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="defaultDestination" ref="maxDestination"/>
        <!-- <property name="receiveTimeout" ref="60000"/> -->
        <property name="messageConverter" ref="memberConverter"/>
    </bean>
:jmsTemplate를 설정한다. 3개의 property값을 눈여겨 봐야 한다.

<!-- client sender
    <bean id="clientSender" class="net.max.msg.impl.ClientSenderImpl">
        <property name="jmsTemplate" ref="jmsTemplate"/>
    </bean>     
     
    <bean id="serverReceiver" class="net.max.msg.impl.ServerReceiverImpl">
        <property name="jmsTemplate" ref="jmsTemplate"/>
    </bean>       
-->
: component-scan 으로 인해 이것들은 필요 없어진다.

<!-- Converter -->
    <bean id="memberConverter" class="net.max.msg.impl.MemberMessageConverter"/>   
: converter는 Sender와 Receiver를 더욱 단순화 시킬수 있다. 만약 컨퍼터가 없으면 아래 Sender와 Receiver 클래스가 좀더 복잡해 진다.



4. 관련 Class 파일

ClientSenderImpl.java

@Service
public class ClientSenderImpl implements ClientSender {

    @Autowired
    private JmsTemplate jmsTemplate;
   
    @Override
    public void sendInfo(final Member member) {
        jmsTemplate.convertAndSend(member);
    }
}



ServerReceiverImpl.java

@Service
public class ServerReceiverImpl implements ServerReceiver {
    @Autowired
    private JmsTemplate jmsTemplate;
      
    public Member receive(){
         return (Member) jmsTemplate.receiveAndConvert();
    }
}



MemberMessageConverter.java

public class MemberMessageConverter implements MessageConverter {
 
    @Override
    public Object fromMessage(Message message) throws JMSException,
            MessageConversionException {
        if(!(message instanceof MapMessage)){
            throw new MessageConversionException("Messaeg isn't a MapMessage");
        }
       
        MapMessage mapMessage = (MapMessage) message;
        Member member = new Member();
        member.setName(mapMessage.getString("name"));
        member.setEmail(mapMessage.getString("email"));
        return member;
    }

    @Override
    public Message toMessage(Object object, Session session) throws JMSException,
            MessageConversionException {
        if(!(object instanceof Member)){
            throw new MessageConversionException("Messaeg isn't a Member");
        }
       
        Member member = (Member) object;
        MapMessage message = session.createMapMessage();
        message.setString("name", member.getName());
        message.setString("email", member.getEmail());
        return message;
    }



ClientSenderTest.java

public class ClientSenderTest {

    @Autowired
    private ClientSender clientSender;
    @Autowired
    private ServerReceiver serverReceiver;
       
    @Test
    public void testClientSender() throws Exception {
        Member member = new Member();
        member.setName("TaeHee1");
        member.setEmail("TaeHee@gmail.com");
        clientSender.sendInfo(member);
        member.setName("TaeHee2");
        clientSender.sendInfo(member);
        member.setName("TaeHee3");
        clientSender.sendInfo(member);
        member.setName("TaeHee4");
        clientSender.sendInfo(member);
        member.setName("TaeHee5");
        clientSender.sendInfo(member);
        member.setName("TaeHee6");
        clientSender.sendInfo(member);
        member.setName("TaeHee7");
        clientSender.sendInfo(member);
       
        Member sm1 = serverReceiver.receive();
        System.out.println("=========>"+sm1.getName());
        assertEquals(member.getEmail(),sm1.getEmail());
    }
}



5. Test 결과
큐에 의해 입력된 순차적으로 메시지를 가져오는것을 확인할수 있었다.

 


6. Resources
            Spring in Action CHAPTER10 Spring messaging

다음에는 Message-Driven(Listener) 기반으로 만들어 본다.
(과거 기선님이 테스트 해본것이 있지만 다시 해본다.)

 

 

test_jms.zip

 

 

 


 

 

 

위의 기본예제-1에 이어서 MDP(Message Driven POJO)로 JMS를 구현해 본다.
javax.jms.MessageListener를 구현하여 onMessage메서드를 만들면 되지만 Spring은 MessageListenerAdapter를 이용하여 보다 청아한(깨끗한-단어좋다) POJO를 만들수 있게 도와 준다.

구현구조

Queue/Topic --> Message Listener Container  --> Message Listener Adapter
                                                                          --(handelMessage())--> POJO



1. Message Listener Container 설정

    <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="destination" ref="maxDestination"/>
        <property name="messageListener" ref="pureMemberMdp"/>
    </bean>



ActiveMQ의 ConnectionFactory 와 엮는다. messageListener 속성은 Message Listener Adapter를 이용한 POJO 클래스이다. 이것은 근본적으로 javax.jms.MessageListener을 구현해야 하나 Spring은 Message Listener Adapter를 써서 POJO스럽게 구현하는걸 도와준다.


2. Message Listener Adapter 설정

    <bean id="pureMemberMdp" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
        <property name="delegate" ref="memberMdp"/>
        <property name="defaultListenerMethod" value="processInfo"/>
        <property name="messageConverter" ref="memberConterver"/>
    </bean>

    <bean id="memberConverter" class="net.max.jms.MemberMessageConverter"/>



MessageListenerContainer에 엮기 위한 Adapter 클래스로 delegate 속성에 실제 엮일 POJO 클래스를 넣어 주며, defaultListenerMethod 속성에 해당 메서드명을 넣는다. 컨버터는 예제1과 동일하다.
이제 마지막으로 POJO 클래스를 작성한다.

3. POJO 클래스 작성

public class MemberMdp {

    public void processInfo(Member member){
        System.out.println("메시지를 받았습니다.----------------");
        System.out.println(member.getName() + "(" + member.getEmail() + ")");
    }
}



말 그대로 POJO 이다.  메시지를 받는 부분은 Spring의 MessageListenerAdapter에 의해서 Simple하게 작성할수 있다.

4. 테스트 해보자.

public class SimpleTest {
    @Autowired
    private JmsTemplate jmsTemplate;
    @Autowired
    private MemberMdp memberMdp;
   
    @Test
    public void testSimple(){
        Member member = new Member();
        member.setName("Max");
        member.setEmail("max@test.com");
        // 보내기
        jmsTemplate.convertAndSend(member);
       
        assertEquals(memberMdp.getMember().getEmail(),member.getEmail());
        System.out.println(memberMdp.getMember().getEmail());
    }
}


메시지 보내는것은 테스트에 하드코딩하고 받는것은 콘솔창에서 확인하게 하였다.
결과 :

메시지를 받았습니다.----------------
Max(max@test.com)
max@test.com


 

 

기존보다 더욱 단순해 졌다.
다음 예제에서는 <jms> 테그로 xml을 더욱 단순화시켜 보자.

 

test_jms_MDP.zip

(*.jar는 Maven으로 대신하고, 이클립스 프로잭트를 통째로 압축한 파일)

 

test_jms.zip
0.01MB
test_jms_MDP.zip
0.01MB