wtorek, 28 sierpnia 2012

ActiveMQ ze springiem

Dziś pokażę jak w springu można skonfigurować JMS korzystając z ActiveMQ. Teorii JMS-a nie będę omawiał, odsyłam do dokumentacji. Spring udostępnia template (podobnie jak do jdbc) pod interfejsem JmsOperations.
Zerknijmy na konfigurację:

<bean id="connectionFactory"
      class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="vm://embedded?broker.persistent=false"></property>
</bean>


<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory"></property>
    <property name="defaultDestination" ref="testQueue"></property>
</bean>


<bean id="testQueue" class="org.apache.activemq.command.ActiveMQQueue">
    <constructor-arg value="pl.turo.spring.jms"></constructor-arg>
</bean>

Widzimy konfigurację fabryki, template oraz testową kolejkę. Parametry konfiguracji fabryki opisane są w dokumentacji ActiveMQ i są dla tej biblioteki specyficzne.

JmsTemplate część przetwarzania deleguje do :
  • MessageConverter - odpowiada za konwersję pomiędzy obiektem przekazanym do wysłania z wiadomością (javax.jms.Message). Domyślny SimpleMessageConverter potrafi konvertować typy: 
    • String - TextMessage 
    •  Serializable - ObjectMessage 
    • Map - MapMessage 
    • byte[] - BytesMessage
  • DestinationResolver - odpowiada za to gdzie wysłać wiadomość (jeśli korzystamy z metod niepodających explicite gdzie wysyłamy). Domyślny to DynamicDestinationResolver
Teraz pytanie jak wysłać wiadomość do kolejki? JmsOperations definiuje kilka metod przy czym najciekawsze są przeciążone metody convertAndSend. Jest najwygodniejsza, ponieważ wrzucamy nasz obiekt (o ile SimpleMessageConverter potrafi go skonwertować) i ewentualnie pokazujemy gdzie chcemy wysłać.

Przykład:
Stwórzmy beana, który będzie nam wysyłał wiadomości:
public class JmsBatchProcessorImpl implements JmsBatchProcessor {

    private JmsOperations jmsOperations;

    public JmsBatchProcessorImpl(JmsOperations jmsOperations) {
        this.jmsOperations = jmsOperations;
    }

    @Override
    public void sentMessage(String message) {
        jmsOperations.convertAndSend(message);
    }
}
Dorzućmy do konfigu springa:
<bean id="jmsBatchProcessor" class="pl.turo.spring.jms.producer.JmsBatchProcessorImpl">
    <constructor-arg ref="jmsTemplate"/>
</bean>

Jak odebrać wiadomość? - Potrzeba nam listenera:
<jms:listener-container>
    <jms:listener ref="messageListener" method="consumeMessage" destination="pl.turo.spring.jms"/>
</jms:listener-container>


<bean id="messageListener" class="pl.turo.spring.jms.consumer.MessageListenerImpl" />

I jeszcze klasa:
public class MessageListenerImpl implements MessageListener {
    private List<String> messages = new ArrayList<String>();

    @Override
    public void consumeMessage(String message) {
        messages.add(message);
    }

    public List<String> getMessages() {
        return messages;
    }
}
MessageListener to mój własny interfejs. Listener nie musi implementować żadnego specjalnego interfejsu, nie ma żadnej adnotacji. Wystarczy konfig w xml-u przedstawiony wyżej. Widać że mój listener trzyma w sobie listę odebranych wiadomości - tak, wiem że to singleton, ale dla przejrzystości przykładu pozwalam sobie na to.
Czyli teraz spring wie gdzie chcemy wysyłać (<property name="defaultDestination" ref="testQueue"/> w jmsTemplate), wie kto chce odebrać wiadomość. Czas sprawdzić czy śmiga. W tym celu postanowiłem napisać prościutką aplikacyjkę webową (w kolejnym poście opiszę jak w mvc postawić aplikację). Pokażę tylko stronę jsp:
<P>Dodaj wiadomość do kolejki: </P>

<form action="sent" name="form" method="post">
  <input type="text" name="value"/>
</form>
<P>Przetworzone wiadomości:<br/> ${messages} </P>
Sewer odbiera z formularza wiadomość i wysyła do kolejki, potrafi też zwrócić odebrane wiadomości:
@Controller
public class HomeController {
  @Autowired
  private JmsBatchProcessor jmsBatchProcessor;

  @Autowired
  private MessageListener messageListener;


  @RequestMapping(value = "/", method = RequestMethod.GET)
  public String home(Locale locale, Model model) {
    model.addAttribute("messages", messageListener.getMessages());
    return "home";
  }


  @RequestMapping(value = "/sent", method = RequestMethod.POST)
  public String sentMessage(@RequestParam("value") String message) {
      jmsBatchProcessor.sentMessage(message);
      return "redirect:/";
  }
}
Jeśli coś w kontrolerze jest niejasne - spokojnie, czytajcie kolejne posty i się rozjaśni. Musicie mi uwierzyć na słowo, że działa, bądź pobierzcie sobie projekt i sami sprawdźcie :-). Postanowiłem projekty udostępniać na githubie:
git://github.com/slturo/spring-jms.git

Brak komentarzy:

Prześlij komentarz