Статьи

Легкий вес встречает тяжелый вес: весна, Groovy и предприятия

Поскольку я готовлюсь к конференции 2G в следующем месяце, одна из вещей, которые меня больше всего поразили, — это то, как гладко Groovy и Spring могут играть вместе. Эти две технологии дают командам разработчиков возможность плавного перехода от облегченных решений к корпоративным.

Любой, кто провел какое-то время вокруг меня, слышал, как я рассуждаю о том, насколько ненужен полноценный стек J2EE для большинства нужд разработки приложений. Хотя многие компании хотели бы полагать, что их приложения являются «необходимыми» для J2EE, в действительности большинство приложений не имеют требований к пропускной способности или транзакциям, необходимых для полноценного стека J2EE. Большинство организаций основывают решение о создании приложений на стеке J2EE не на реальных технических требованиях, а на необходимости обнять гигантского (часто синего) плюшевого мишку J2EE и надеются, что это защитит их от страшной ночи.


Groovy и Spring позволяют нам быстро создавать приложения J2EE без дополнительных затрат. Что еще более важно, объединение этих двух технологий дает нам плавный переход к корпоративному решению. Это критическая точка:

Большинство архитектурных групп стремятся найти единый стек разработки одного размера, а не искать стек разработки, который позволяет им переходить от легких к тяжеловесным реализациям с минимальными трудностями миграции. Применение одного размера для всего технического стека значительно снижает конечную стоимость доставки решения.

Боб смиренного сообщения

Мне нравится EJB Message Drive Bean. Я думаю, что это очень элегантное решение для создания простых асинхронных вызовов. Тем не менее, они могут быть большой болью для установки в контейнере J2EE. Часто я хочу решения и хочу быстро. Итак, давайте посмотрим, как мы можем написать решение Spring / Groovy, которое предоставляет те же возможности, что и J2EE MDB. Для этого нам необходимо выполнить следующие действия:

  1. Напишите класс на основе Groovy, который будет действовать как наш MDB.
  2. Настроить Spring для настройки очереди (в этом случае я использую ActiveMQ )
  3. Настройте Spring для связи очереди и нашего класса MDB.
  4. Напишите фрагмент кода клиента, чтобы отправить сообщение в очередь.

Написание класса MDB

Для нашего класса MDB я собираюсь использовать один из примеров из моих выступлений. Этот MDB будет принимать данные, переданные в очередь, а затем использовать DAO для обновления записи. Пример, который я показываю ниже, берет первичный ключ обновляемой записи (например, идентификатор сотрудника), а затем обновляет адрес сотрудника значением lat / long.

package com.netchange.racerhino.map


import javax.jms.Message
import javax.jms.MessageListener

class UpdateFieldRepMDB implements MessageListener{

def fieldRepService
public void onMessage(Message pMessage){
def holderLocation = new Location(
latitude: new Double(pMessage.getString("latitude")).doubleValue(),
longitude:new Double(pMessage.getString("longitude")). doubleValue())

def fieldRep = new FieldRepresentative(employeeId:pMessage.getString("employeeId"),
location:holderLocation)
fieldRepService.updateFieldRepsLatLong( fieldRep)
}
}


Главное, на что нужно обратить внимание в приведенном выше примере кода, это то, что я должен убедиться, что мой класс Groovy реализует интерфейс MessageListener. После того, как у меня написан MDB выше, мне нужно зарегистрировать его в конфигурационном файле Spring. Запись для этого компонента будет выглядеть примерно так:

  <lang:groovy id="UpdateFieldRepMDB" 
script-source="classpath:/com/netchange/racerhino/map/UpdateFieldRepMDB.groovy">
<lang:property name="fieldRepService" ref="FieldRepService"/>
</lang:groovy>

 

Теперь это одна из красавиц весны. Я только что зарегистрировал свой класс Groovy как бин в Spring. Spring рассматривает мой класс Groovy как любой другой боб Spring. Мой класс Groovy теперь может в полной мере участвовать во всех сервисах, предлагаемых Spring. Это включает в себя использование класса в качестве прослушивателя для очереди сообщений.

Настройка Spring для использования очереди

For my example here, I am going to use the Apache ActiveMQ project. This project provides a fully functional, JMS compliant message queue that is incredibly easy to setup. To use ActiveMQ, I literally have to download the distribution and fire up the activemq script in the bin directory and away I go. (Obviously there is far more I want to do here to operationalize my queue, but that is outside the scope of what I am doing here.)

So lets configure Spring to use ActiveMQ and setup a queue. To do this I am going to need to add two

tags to my Spring configuration file.


 
  <bean id="mqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
  
<property name="brokerURL" value="tcp://JCC.local:61616"/>
</bean>

<bean id="fieldRepDest" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="fieldrepresentative.updatelatlong.queue"/>
</bean>


The first

tag defines the connection factory and URL Spring is going to use when sending a message. The second tag defines an actual queue that will be available when Spring attempts to communicate with ActiveMQ. In our example above, we have one MQ called «fieldrepresentative.updatelatlong.queue» This queue will be automatically created if it does not already exist. The next thing we are going to want to do is «hook» our MDB up to our queue.



Making the Hookup



Hooking up our MDQ to our queue requires an additional

tag in our Spring configuration file.

<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="mqConnectionFactory"/>
<property name="destination" ref="fieldRepDest"/>
<property name="messageListener" ref="UpdateFieldRepMDB"/>
</bean>

Spring uses the concept of a ListenerContainer to bring together an MQ connection, a target queue and the Spring-enabled class that will process messages off of the queue. In the example above the «connectionFactory» property defines a reference to the connection we will use to communicate to our ActiveMQ instance. The «destination» property points to what queue is going to be listened to and the «messageListener» defines the Spring bean (in this case our Groovy class) that will be process the record.

Spring offers a number of different ListenerContainers that can be configured. The example shown above is the most basic type and is not transactional. However, Spring does offer a full-blown JTA-compliant ListenerContainer that can participate in transactions.






Finally the Client Code







To actually write to the queue is incredibly easy. The code snippet below is using straight JMS calls from within a Groovy-based unit test. I could use a Spring JMS template, but I chose to explicitly use the JMS calls to dump a message on a queue.

def connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616")
def connection =connectionFactory.createConnection()

def session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE)
def dest = new ActiveMQQueue("fieldrepresentative.updatelatlong.queue")
def producer = session.createProducer(dest)
connection.start()

def MapMessage message = session.createMapMessage()
message.setString("employeeId", "TS12345")
message.setString("latitude", "12345")
message.setString("longitude", "97874")

producer.send(message)

session.close()
connection.close()

One of the key take aways above is that we were able to build a solution that offered J2EE like capability and a smooth enterprise migration path. All of the code is written in Groovy which means if necessary it could be compiled down to Java bytecode and mixed and matched with Java classes. In addition, we can always rip the Spring-based MDB out and put in a J2EE based MDB solution. Our applications will never know the difference.






Spring acts as an intermediation layer that abstracts away the implementation details of how things are called. Groovy provides us with the language velocity needed to deliver solutions quickly without having to deal with the normal «gunk» associated with building a statically-based Java/J2EE solutions.