Введение в Spring Roo: Часть 2. Разработка приложения при помощи Spring Roo

Создание полноценного корпоративного приложения при помощи Spring Roo

В первой части серии мы увидели, как за несколько минут создать CRUD-приложение при помощи Roo. Во второй части мы преобразуем его в полноценное корпоративное приложение, добавив систему безопасности Spring, поддержку электронной почты и многое другое.

Шекхар Гулати, старший консультант, Xebia

Шекхар Гулати (Shekhar Gulati) работает Java-консультантом в Xebia India. Более шести лет он занимается корпоративными Java-приложениями. Имеет обширный опыт работы со Spring-проектами, такими как Spring, Spring-WS и Spring Roo. В сферу его интересов входят Spring, базы данных NoSQL, Hadoop, RAD-среды (такие как Spring Roo), облачные вычисления (в основном PaaS-сервисы, такие как Google App Engine, CloudFoundry, OpenShift). Является активным автором статей для JavaLobby, Developer.com, IBM developerWorks и своего собственного блога http://whyjava.wordpress.com/. Связаться с ним можно в Твиттере (@ http://twitter.com/#!/shekhargulati).



20.02.2013

В первой части серии статей, посвященных инструментарию Spring Roo, мы создали с помощью Spring Roo небольшое корпоративное приложение conference. Теперь при помощи Spring Roo мы преобразуем это простое Web-приложение с функциями CRUD в полноценное корпоративное приложение. Перед началом работы убедитесь, что у вас установлен Spring Roo и загружен SpringSource Tool Suite (дополнительная информация приведена в первой части).

Итак, приступим

Чтобы преобразовать приложение conference, необходимо воссоздать его. Выполните указанные ранее инструкции либо используйте команду Roo script. Команда script выполняет все команды, имеющиеся в файле ресурсов. Если вы следовали указаниям первой части серии, то заметили, что Roo создал файл log.roo, который содержит все команды, выполненные в Roo Shell. Мы выполним файл log.roo и воссоздадим приложение.

  1. Этот файл включен в пример исходного кода. Можете переименовать его в conference.roo.
  2. Создайте новый каталог conference и скопируйте в него файл conference.roo.
  3. Откройте процессор командной строки вашей операционной системы.
  4. Перейдите в только что созданный каталог conference.
  5. Выполните команду script --file conference.roo.

Команда script воссоздаст приложение за несколько секунд, если у вас имеются необходимые JAR-файлы в репозитории Maven. В противном случае потребуется больше времени, поскольку нужно будет загрузить все эти JAR-файлы. Команда script полезна и потому, что ее можно использовать в качестве шаблона для создания Spring-проектов.

Прежде чем двинуться дальше, выполните импорт Maven-проекта в STS. STS поставляется в одном пакете с плагином Maven Eclipse. Для импорта проекта выберите File > Import > Maven > Existing Maven Projects, а затем каталог проекта. Мы выполняем импорт проекта в STS, поскольку позднее напишем специализированный код.

Созданное нами Web-приложение работает, и мы можем протестировать его вручную, создав, прочитав, обновив и удалив объекты Speaker и Talk. Но хорошо было бы автоматизировать этот процесс.


Автоматизированное Web-тестирование

Здесь приходит на помощь предоставляемая Spring Roo функциональность поддержки тестирования Selenium. Selenium – это набор надежных инструментальных средств, поддерживающих быструю разработку автоматизированных тестов для Web-приложений. Для добавления в приложение поддержки тестирования Selenium выполните следующую команду:

	selenium test --controller ~.web.SpeakerController
	selenium test --controller ~.web.TalkController

Команда selenium test создаст Selenium-тест для контроллеров Speaker и Talk. Эта команда имеет один обязательный атрибут controller, указывающий имя контроллера для создания Selenium-теста. Эта команда также имеет два необязательных атрибута name и serverUrl, указывающих название Selenium-теста и сервер, на котором размещено Web-приложение. При выполнении команды selenium test Spring Roo добавит также плагин Selenium Maven.

Ранее мы создали варианты Selenium-теста для нашего контроллера, но перед их выполнением нам необходимо исправить маленький дефект в наборе Selenium-тестов, созданном Spring Roo. Мы добавили в наш объект Speaker ограничение, что возраст докладчика должен быть в пределах от 25 до 60, но набор тестов не принимает во внимание это ограничение. Он использует для возраста значение 1, поэтому тест завершится неудачно. Нам нужно изменить файл test-speaker.xhtml и изменить раздел так, как показано в листинге 1.

Листинг 1. Изменение test-speaker.xhtml
		<tr>
			<td>type</td>
			<td>_age_id</td>
			<td>1</td>
		</tr>

на

		<tr>
			<td>type</td>
			<td>_age_id</td>
			<td>26</td>
		</tr>

Этот дефект будет исправлен в следующей версии Spring Roo.

Для выполнения Selenium-тестов необходимо запустить сервер Tomcat. Это можно сделать при помощи команды Maven mvn tomcat:run. По умолчанию все Web-приложения, созданные при помощи Roo, имеют Maven-плагин для Web-серверов Tomcat и Jetty. Для запуска selenium test выполните команду Maven mvn selenium:selenese.

Она запустит браузер Firefox для выполнения Selenium-тестов. Во время выполнения тестов должно отобразиться что-то похожее на рисунок 1.

Рисунок 1. Selenium-тесты
Рисунок 1. Selenium-тесты

Теперь каждый может получить доступ к приложению и выполнить операции создания, обновления и удаления объектов Speaker и Talk. В приложениях, работающих в режиме реального времени, используется система безопасности, определяющая, кто и какие операции может выполнять.


Защита Web-приложения

Roo использует систему безопасности Spring Security, функциональность которой добавляется в приложение одной строкой. Spring Security – это мощная и хорошо настраиваемая среда аутентификации и управления доступом. Она является фактическим стандартом безопасности для Spring-приложений.

Добавление функциональности Spring Security

Для добавления Spring Security выполните команду security setup.

Эта команда добавит все необходимые JAR-файлы Spring Security и настроит базовую защиту вашего приложения. Эта команда также создаст и другие файлы, из которых особенно важным является applicationContext-security.xml, содержащий все определения bean-компонентов, относящихся к системе безопасности. Содержимое applicationContext-security.xml выглядит примерно так, как показано в листинге 2. Я заменил пароль точками для лучшей читаемости.

Листинг 2. Содержимое файла applicationContext-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans \
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/security \
http://www.springframework.org/schema/security/spring-security-3.0.xsd">

	<!-- Конфигурации системы безопасности HTTP -->
    <http auto-config="true" use-expressions="true">
    	<form-login login-processing-url="/resources/j_spring_security_check" \
login-page="/login" authentication-failure-url="/login?login_error=t"/>
        <logout logout-url="/resources/j_spring_security_logout"/>
        
        <!-- Настройте эти элементы для защиты URI в вашем приложении -->
        <intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')"/>
        <intercept-url pattern="/member/**" access="isAuthenticated()" />
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/**" access="permitAll" />
    </http>

	<!-- Настройте механизм аутентификации -->
    <authentication-manager alias="authenticationManager">
    	<!-- SHA-256-значения можно сгенерировать, используя \
'echo -n your_desired_password | sha256sum' \
(using normal *nix environments) -->
    	<authentication-provider>
	    	<password-encoder hash="sha-256"/>
	        <user-service>
	            <user name="admin" password="..." authorities="ROLE_ADMIN"/>
		        <user name="user" password="..." authorities="ROLE_USER"/>
		    </user-service>
    	</authentication-provider>
	</authentication-manager>

</beans:beans>

Система безопасности, настроенная Roo, является обобщенной и не привязана к нашему приложению. Помните, что Roo помогает установить и настроить приложение для быстрого начала работы, но ответственность за специализированную настройку конечного продукта лежит на разработчике. В нашем случае Roo просто предоставил шаблон для Spring Security, а его настройка под наши требования – это наша задача.

Настройка Spring Security

В нашем приложении любой пользователь может создать объект Speaker, но только Speaker может создать Talk. Нам необходимо изменить applicationContext-security.xml так, как показано ниже. В листинге 3 приведен только тот фрагмент XML, который нужно изменить.

Листинг 3. Изменение applicationContext-security.xml
<http auto-config="true" use-expressions="true">
    	<form-login login-processing-url="/resources/j_spring_security_check" \
login-page="/login" authentication-failure-url="/login?login_error=t"/>
        <logout logout-url="/resources/j_spring_security_logout"/>
        
        <!-- Настройте эти элементы для защиты URI в вашем приложении -->
        <intercept-url pattern="/talks/**" access="hasRole('ROLE_USER')"/>
        <intercept-url pattern="/speakers/**" access="permitAll" />
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/**" access="permitAll" />
</http>

Я изменил intercept-url так, чтобы только пользователи с ролью user могли создавать Talk, а регистрироваться как Speaker могли все пользователи.

В сгенерированной ранее посредством Roo системе Spring Security используется встроенный поставщик аутентификации, конфигурация которого прописана в теге <user-service>. Поскольку наше приложение управляет объектами Speaker, мы должны создать специального поставщика аутентификации, использующего данные Speaker. Для аутентификации мы будем использовать адрес электронной почты Speaker в качестве имени пользователя и добавим в логический объект Speaker поле password, которое мы будем использовать в качестве пароля аутентификации.

Я добавляю поля password в логический объект Speaker с помощью Roo Shell:

field string --class ~.domain.Speaker --fieldName password --notNull --sizeMin 6 –sizeMax
10

Я также добавил следующее ограничение: поле password не должно быть пустым и должно иметь от 6 до 10 символов.

Поскольку в качестве аутентификационных параметров используются поля email и password, мы должны найти объект Speaker с соответствующими адресом электронной почты и паролем. Spring Roo позволяет создавать методы поиска (finder) для приложения при помощи команды finder add:

finder add --finderName findSpeakersByEmailAndPasswordEquals --class ~.domain.Speaker

Можно найти все методы поиска для логического объекта, используя команду finder list. Команда finder add записывает код метода поиска в файл Speaker_Roo_Finder.aj, а также некоторые файлы, связанные с просмотром. Это позволяет искать объекты Speaker при помощи графического интерфейса.

Написание специализированного AuthenticationProvider

Мы напишем код специализированного поставщика аутентификации, расширяя класс AbstractUserDetailsAuthenticationProvider, который реализует аутентификацию посредством имени пользователя и пароля. Классы, расширяющие AbstractUserDetailsAuthenticationProvider, должны реализовывать два его абстрактных метода – additionalAuthenticationChecks и retrieveUser. Поставщик вызывает метод retrieveUser для аутентификации объекта Speaker, используя введенные адрес электронной почты и пароль. Поиск объекта Speaker в базе данных осуществляется созданным ранее методом поиска. Если объект Speaker найден, ему назначается GrantedAuthority ROLE_USER. При успешной аутентификации пользователя этот метод возвращает заполненный объект UserDetails либо генерирует исключительную ситуацию BadCredentialsException с соответствующим сообщением в противном случае (см. листинг 4).

Листинг 4. Специализированная аутентификация
package com.dw.roo.conference.security;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityNotFoundException;
import javax.persistence.NonUniqueResultException;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.\
dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import com.dw.roo.conference.domain.Speaker;

public class ConferenceAuthenticationProvider extends \
AbstractUserDetailsAuthenticationProvider {

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, \
UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        // TODO Автоматически сгенерированная заглушка метода

    }

    @Override
    protected UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken \
authentication) throws AuthenticationException {
        String password = (String) authentication.getCredentials();
        if (!StringUtils.hasText(password)) {
            throw new BadCredentialsException("Please enter password");
        }
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        try {
            Speaker speaker = Speaker.findSpeakersByEmailAndPasswordEquals(username, \
password).getSingleResult();
            authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
        } catch (EmptyResultDataAccessException e) {
            throw new BadCredentialsException("Invalid username or password");
        } catch (EntityNotFoundException e) {
            throw new BadCredentialsException("Invalid user");
        } catch (NonUniqueResultException e) {
            throw new BadCredentialsException("Non-unique user, contact administrator");
        }
        return new User(username, password, true, // разрешено
                true, // учетная запись не просрочена
                true, // учетные данные не просрочены
                true, // учетная запись не заблокирована
                authorities);
    }
}

В файле applicationContext-security.xml мы должны определить bean-компонент conferenceAuthenticationProvider и заменить встроенного поставщика аутентификации, сгенерированного Roo, на наш conferenceAuthenticationProvider (см. листинг 5).

Листинг 5. conferenceAuthenticationProvider
<beans:bean name="conferenceAuthenticationProvider"
class="com.dw.roo.conference.security.ConferenceAuthenticationProvider">
</beans:bean>

<!-- Настройте механизм аутентификации -->
<authentication-manager alias="authenticationManager">
	<authentication-provider ref="conferenceAuthenticationProvider"/>
</authentication-manager>

Теперь, если запустить сервер и попытаться создать объект Talk, отобразится экран входа в систему, где нужно ввести адрес электронной почты и пароль созданного нами логического объекта Speaker. Мы не ставили целью создать идеального поставщика аутентификации, а хотели показать, что делает Roo и что вы должны сделать самостоятельно.


Уведомления по электронной почте

В нашем приложении объекты Speaker должны получить уведомление при создании Talk, поэтому давайте добавим в наше приложение поддержку электронной почты. В качестве SMTP-сервера мы будем использовать Gmail, чтобы сконцентрироваться на отправке сообщений при помощи Roo. Добавление поддержки электронной почты в приложение осуществляется при помощи следующей команды:

email sender setup --hostServer smtp.gmail.com --username \
<Ваш email> --password <Ваш пароль> --port 587 --protocol SMTP

Команда email sender устанавливает в проекте Spring JavaMailSender. Свойства, имеющие отношение к электронной почте, можно изменить в файле email.properties.

После создания Talk нам необходимо отправить письмо. Для этого следует добавить поле email в TalkController. Для добавления поля email выполните команду:

field email template --class ~.web.TalkController

Эта команда добавит шаблон MailSender и метод sendMessage в TalkController. Теперь нам нужно активизировать метод sendMessage после сохранения Talk в базе данных. Поскольку весь код для TalkController находится в файле TalkController_Roo_Controller.aj, самый простой способ выполнить эту задачу – создать метод encodeUrlPathSegment из .aj-файла для класса TalkController и добавить вызов метода sendMessage после строки talk.persist() (см. листинг 6).

Листинг 6. Создание метода encodeUrlPathSegment из .aj-файла для TalkController
public class TalkController {

	@Autowired
	private transient MailSender mailTemplate;

	public void sendMessage(String mailFrom, String subject, String mailTo,
			String message) {
		org.springframework.mail.SimpleMailMessage \
simpleMailMessage = new org.springframework.mail.SimpleMailMessage();
		simpleMailMessage.setFrom(mailFrom);
		simpleMailMessage.setSubject(subject);
		simpleMailMessage.setTo(mailTo);
		simpleMailMessage.setText(message);
		mailTemplate.send(simpleMailMessage);
	}

	@RequestMapping(method = RequestMethod.POST)
	public String create(@Valid Talk talk, BindingResult result, Model model,
			HttpServletRequest request) {
		if (result.hasErrors()) {
			model.addAttribute("talk", talk);
			return "talks/create";
		}
		talk.persist();
		sendMessage("spring.roo.playground@gmail.com", "Your talk is created",
				talk.getSpeaker().getEmail(), \
"Congrats your talk is created");
		return "redirect:/talks/"
				+ encodeUrlPathSegment(talk.getId().toString(), request);
	}

	private String encodeUrlPathSegment(String pathSegment,
			HttpServletRequest request) {
		String enc = request.getCharacterEncoding();
		if (enc == null) {
			enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
		}
		try {
			pathSegment = UriUtils.encodePathSegment(pathSegment, enc);
		} catch (UnsupportedEncodingException uee) {
		}
		return pathSegment;
	}
}

Теперь пользователи Speaker будут принимать уведомления о создании объекта Talk на указанный в их учетной записи адрес электронной почты.


Поддержка интернационализации

Поскольку мы создаем Web-приложение для Интернета, важно поддерживать различные языки, чтобы с нашим приложением могли работать пользователи из разных стран. Spring Roo добавляет поддержку интернационализации посредством команды web mvc install language, которая устанавливает в приложении новый язык. Например, команды для испанского и итальянского выглядят так:

web mvc install language --code es 
web mvc install language --code it

В настоящее время Roo поддерживает шесть языков, и вы можете самостоятельно написать дополнение для любого другого языка. Теперь во время работы приложения вместе с флагом Великобритании отображаются флаги Италии и Испании. Если вы выберете любой из этих флагов, Web-приложение будет отображаться на соответствующем языке.


Социализация Web-приложения

Мы живем в эпоху социальных сетей, поэтому в современные приложения обычно добавляются социальные функции. Имеет смысл добавить видеоматериалы объектов Talk. Roo предоставляет поддержку встраивания видеоматериалов, загруженных на YouTube, Vimeo, Viddler, Google Video и т.д. Для этого используется следующая команда:

web mvc embed video --provider VIMEO --videoId 16069687

После запуска сервера и приложения в браузере мы сможем просматривать эти встроенные видеоматериалы. Аналогично можно добавить видео с YouTube или Viddler.

Roo также поддерживает встраивание в приложение документов, котировок акций, карт, фотографий, видеопотоков и Твиттер-сообщений. В листинге 7 приведены различные команды embed.

Листинг 7. Команды embed
web mvc embed document 
web mvc embed finances 
web mvc embed map
web mvc embed photos 
web mvc embed stream video 
web mvc embed twitter
web mvc embed video

Реверсивное проектирование базы данных

Реверсивное проектирование базы данных (database reverse engineering – DBRE) позволяет проанализировать существующую базу данных и открыть ее для доступа в виде приложения. Чтобы продемонстрировать работу DBRE, я создам приложение обратной связи feedback из существующей схемы feedback. В качестве базы данных я буду использовать MySQL.

Перед запуском Roo необходимо создать схему в нашей MySQL. Выполните SQL-сценарий, приведенный в листинге 8, для создания схемы feedback в базе данных MySQL.

Листинг 8. SQL-сценарий создания схемы feedback
create database feedback_schema;
use feedback_schema;
CREATE TABLE feedback (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  TalkTitle VARCHAR(45) NOT NULL,
  SpeakerName VARCHAR(45) NOT NULL,
  Feedback VARCHAR(4000) NOT NULL,
  PRIMARY KEY (id)
)
ENGINE = InnoDB;

Это простая схема с одной таблицей и без каких-либо взаимосвязей, но Roo может успешно выполнять реверсивное проектирование сложных схем, состоящих из множества таблиц и взаимосвязей.

После выполнения приведенного выше SQL-сценария и генерирования схемы создайте приложение feedback:

  1. Создайте каталог feedback.
  2. В командной строке вашей операционной системы перейдите в каталог feedback.
  3. Запустите Roo Shell, выполнив команду roo.
  4. Выполните команду project --topLevelPackage com.dw.roo.feedback для создания нового Maven-проекта.
  5. В приложении в качестве базы данных мы будем использовать MySQL. Для настройки персистенции используйте следующие команды:
    persistence setup --provider HIBERNATE 
    --database MYSQL --databaseName feedback_schema 
      --userName root --password password

    Поскольку я создал схему как пользователь root, я использую root в качестве имени пользователя. Введите имя пользователя и пароль, с которыми создавали схему. Эта команда добавит также все JAR-файлы, необходимые для персистенции.

  6. Схему базы данных можно проанализировать при помощи команды database introspect --schema feedback_schema. Команда database introspect показывает метаданные схемы базы данных. Эти метаданные отображаются в консоли Roo Shell. Можно также экспортировать xml метаданных в файл, используя атрибут --file.
  7. После анализа схемы базы данных можно выполнить реверсивное проектирование, используя команду database reverse engineer --schema feedback_schema --package ~.domain.

    Команда database reverse engineer имеет два обязательных атрибута, schema и package, предназначенных для указания названия схемы и пакета, в который Roo будет генерировать исходные файлы. Эта команда создаст все логические объекты в пакете com.dw.roo.feedback.domain.

  8. Следующий шаг – генерирование контроллера для приложения. Это можно сделать при помощи команды controller all --package ~.web.
  9. Перед запуском приложения необходимо сделать небольшое изменение в одном свойстве в файле persistence.xml. Свойство hibernate.ejb.naming_strategy использует значение ImprovedNamingStrategy, которое не работает для баз данных MySQL, поэтому при выполнении команды mvn clean install tomcat:run возникнет исключительная ситуация. Чтобы исправить положение, необходимо изменить hiberate.ejb.naming_strategy на DefaultNamingStrategy, как показано ниже:
    <property name="hibernate.ejb.naming_strategy" 
    value="org.hibernate.cfg.DefaultNamingStrategy"/>
  10. Теперь можно запустить приложение feedback, выполнив команду Maven mvn clean install tomcat:run.

Исходный код приложений conference и feedback можно загрузить по ссылкам, приведенным в разделе Ресурсы.


Заключение

Итак, мы трансформировали наше простое CRUD Web-приложение в полноценное корпоративное приложение. Мы продемонстрировали, насколько просто можно добавить в него такие функциональные возможности как Selenium-тестирование, система Spring Security, поддержка интернационализации и т.д. Также было показано, как создать приложение из существующей базы данных, используя функциональность реверсивного проектирования баз данных из Spring Roo. Есть и много других функциональных возможностей, таких как поддержка JMS, Solr, JSON и т.д., которые легко можно добавить в приложение.

В третьей части серии я расскажу, как перенести приложение conference в Google App Engine.


Загрузка

ОписаниеИмяРазмер
Исходный код к части 2os-springroo2-sample_code.zip11 КБ

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source, Технология Java
ArticleID=858628
ArticleTitle=Введение в Spring Roo: Часть 2. Разработка приложения при помощи Spring Roo
publish-date=02202013