понедельник, 29 декабря 2008 г.

Тестирование Spring приложений

Эта статья - заготовка, для быстрой организации тестирования спринг приложений.
Рассмотрим типичную конфигурацию Spring + Hibernate. Для тестирования БЛ будет использоваться JUnit4. Заполнять БД тестовыми данными будем при помощи DBUnit. Для запуска тестов будет использоватся Ant(на билдсервере) и Eclipse (для дебага).
Полезная л-ра:
Класная статья о тестировании Spring-a в JUnit-е http://samolisov.blogspot.com/2008/07/spring-junit.html
Класная статья о JUnit4 http://www.ibm.com/developerworks/ru/edu/j-junit4/index.html
Итак нам понадобятся следующие библиотеки:
  1. драйвер БД (в моем примере mqsql-5.0.4)
  2. hibernate 3.3.1, hibernate-annotations 3.4
  3. spring-2.5.5
  4. для работы спринга: cglib 2.1, commons-lang 2.4, spring-modules 0.9
  5. для тестирования спринга в JUnit 4: spring-test (из spring-modules)
  6. junit 4.4
  7. логирование: commons-loging 1.0.4, commons-login-api 1.0.4, log4j 1.2.13
  8. заполнение тестовой БД-х: dbUnit 2.4.2
В заготовке используется схема из двух таблиц "bookcase" и "book", с типичной связью один ко многим. Бизнес модель состоит из трех модулей: млодуля сервисов, модели и DAO. Модель описывает сущности, DAO - описывает методы сохранения и выборки сущностей из БД используя Hibernate Criteria Queries (обекстную модель построения запросов), сервисы же предоставляют высокоуровневые бизнес методы. Для реализации бизнес методов сервисы используют DAO, но также они могут использовать и бизнес методы других сервисов. Так в заготовке "BookcaseService" использует методы другого сервиса "AnotherService".

Для тестирования сервиса "BookcaseService" используется класс "BookceseServiceTest". В нем есть пример:
  • использования dbunit при тестировании
  • создания сессии для использования в тестах обьектов использующих "hibernate lazy loading"
  • замещения сервиса ""AnotherService" сервисом "AnotherTestOverrideService" для тестов
Ant-ский билд скрипт содержит цели для создания БД, запуска юнит тестов и построения веб репортов их результатов, создания javadocs по проекту, построения веб представления метрики проекта (сколько пакетов, класов и т.д., для етого используется утилита jdepend), а также svn статистики (используется statsvn)

Скачать исходники проекта с библиотеками (14мб)
Скачать исходники без библиотек

среда, 24 декабря 2008 г.

Библиотека urlrewrite

Недавно столкнулся с очень приятной библиотекой http://tuckey.org/urlrewrite/. Она представляет собой обычный фильтр который позволяет задавать свои настройки в хмл файле. Прелесть этой вещицы во первых в простоте (от первого знакомства до использования не более 5 минут), а во вторых в функционале (сейчас я с трудом себе могу представить как мои веб приложения обходились без нее).

Главная фича даной библиотеки: вы можете переформатировать запрос пользователя до не узнаваемости и затем перенаправить на нужный вам адрес. Это полезно во первых если вы хотите предоставить структурированые каким либо образом url-ы пользователю, во вторых, если вам просто нужно средиректить пользователя на другой адрес который имеет специфический формат и в третьих если ваши картинки, стили и ява скрипты запрашиваются клиентом по одному адресу, а вам по каким-либо причинам их удобно держать в другом.

Инсталяция: на сайте проэкта все детально описано, состоит из 3-х шагов: бросьте в папку WEB-INF/lib саму библиотеку urlrewrite.jar, в папку WEB-INF файл urlrewrite.xml (в котором вы будете описывать правила работы фильтра), и добавьте магические строки в web.xml:
1 <filter>
2 <filter-name>UrlRewriteFilter</filter-name>
3 <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
4 <init-param>
5 <param-name>logLevel</param-name>
6 <param-value>DEBUG</param-value>
7 </init-param>
8 <init-param>
9 <param-name>confReloadCheckInterval</param-name>
10 <param-value>5</param-value>
11 </init-param>
12 </filter>
13 <filter-mapping>
14 <filter-name>UrlRewriteFilter</filter-name>
15 <url-pattern>/*</url-pattern>
16 </filter-mapping>
в строке 6 указывается необязательный параметр - уровень вывода логов, а в строке 10 - интервал перечитывания файла urlrewrite.xml (даная конфигурация позволяет изменять его на лету, очень полезно на этапе разработки).

Конфигурирование файла urlrewrite.xml:
  1. может содержать несколько тегов rule, при этом они будут выполнятся один за другим сверху в низ
  2. для задания адресов используется Java Regular Expression. Кратко о синтаксисе можно прочитать например тут http://htmlweb.ru/java/regexp.php
  3. тег from - задает адреса на которые будет срабатывать фильтр
  4. тег to - указывает адрес куда будет перенаправлен пользователь (если используете redirect может пригодится внутренняя переменная %{context-path} - содержащая контекст вашего приложения).
    Пример:
    Если ваше приложение находится по адресу localhost/myapp то
    1 <to type="redirect">%{context-path}/index.jsp</to>
    пренаправит пользователя по адресу localhost/myapp/index.jsp
  5. condition - позволяет добавить дополнительные параметры, для того чтоб ограничить запросы на которые будет срабатывать фильтр.
    Пример:
    1 <condition type="request-uri" operator="notequal">/manager</condition>
    указывает что фильтр не должен срабатывать на запросы в которых встречается комбинация "/manager"
  6. set - позволяет записать информацыю в сессию, запрос, куки и т.д.
    Пример:
    1 <set name="publisherDomain" type="session">myDomain</set>
    записывает строку "myDomain" в переменную publisherDomain.
Маленький экскурс в регулярные выражения:
  • ^ - начало строки (пример: ^hi - значит что строка должна начинатся фразой "hi"
  • $ - конец строки

  • . - любой символ
  • [abc] - любой символ из заключенных в квадратные скобки
  • [^abc] - любой символ кроме заключенных в квадратные скобки
    можно также указать диапазон или тип символов

  • * - повторение символа 0 или более раз. (пример: ^/.* - строка начинается с / за которым следует любая комбинация символов)
  • + - как и * но обязательное повторение 1 раз или более
  • ? - повторение 0 или 1 раз

  • () -запомнить найденное выражение
  • $1, $2, ... - использовать запомненное выражение
    Пример:
    1 <rule>
    2 <from>^/(.+)$</from>
    3 <to type="redirect">/$1/login.html</to>
    4 </rule>
    Фильтр будет срабатывать на все адреса начинающиеся с "/" за которым следует хотя бы один символ и запоминать символы после "/". Т.е. если пользователь введет адрес /test, то фильтр перенаправит его на страницу /test/login.html
Для закрепления пример файла urlrewrite.xml:

1 <?xml version="1.0" encoding="utf-8"?>
2 <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN"
3
"http://tuckey.org/res/dtds/urlrewrite3.2.dtd">

4
5 <urlrewrite>
6
7 <rule>
8 <from>^/portal$</from>
9 <to type="redirect">%{context-path}/portal/index.jsp</to>
10 </rule>
11
12 <rule>
13 <condition type="request-uri" operator="notequal">/portal</condition>
14 <condition type="request-uri" operator="notequal">/css</condition>
15 <condition type="request-uri" operator="notequal">/js</condition>
16 <condition type="request-uri" operator="notequal">/img</condition>
17 <condition type="request-uri" operator="notequal">/html</condition>
18 <from>^.*/(.*)/(.*)$</from>
19 <set name="publisherDomain" type="session">$1</set>
20 <to type="redirect">%{context-path}/portal/$2</to>
21 </rule>
22
23 </urlrewrite>
здесь первое правило будет срабатывать на запросы типа /portal и перенаправлять их на страницу /portal/index.jsp
Второе правило будет срабатывать на все остальные урлы кроме обращений связаных с загруской стилей, стат. страниц, картинок и т.д., далее оно будет вырезать выражение находящееся между первым и последним знаком "/"? и помещать его в сесию, а пользователя перенаправлять по адресу /myapp/portal/выражение_стоящее_за_последним_знаком_/

вторник, 23 декабря 2008 г.

Конвертация java кода в html

Не нашел нормальных средств для вставки java, xml, html, текстов в стандартном блоговском редакторе сообщений. Пришлось искать доп. средства. Пока использую http://www.palfrader.org/code2html/code2html.html хотя страшно не нравится. Хотелось бы иметь конвертор который один раз генерит стили и затем цепляет их к тексту, да еще чтоб и работало сходу под виндой. Из чего то похожего нашлось только java-code-export/. Класная програма жаль только она стили для каждого явовского файла каждый раз по разному генерит :)

Вызов спринговых бинов из фильтра

Если возникает необходимость вызова спринговых сервисов из фильтров можно использовать класс org.springframework.web.context.support.WebApplicationContextUtils, который позволяет обращатся к бинам по имени. По умолчанию имя бина совпадает с именем класа но начинается с маленькой буквы.
Например имеется бин PublisherService:

1 @Service
2 public class PublisherService {
3
4 @Autowired
5 private PubliherDAO publisherDAO;
6
7 //...
8 }

тогда его можно вызвать из фильтра следующим образом

1 public class MyFilter implements Filter {
2
3 private final Log log = LogFactory.getLog(getClass());
4
5 private PublisherService publisherService;
6
7 public void init(FilterConfig config) throws javax.servlet.ServletException {
8 if (null == publisherService) {
9 publisherService = (PublisherService) WebApplicationContextUtils
10 .getWebApplicationContext(config.getServletContext()).getBean(
11 "publisherService");
12 }
13 }
14
15 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
16 throws IOException, ServletException {
17
18 HttpServletRequest request = (HttpServletRequest) req;
19 HttpServletResponse response = (HttpServletResponse) resp;
20
21 // to do something with publisherService
22
23 chain.doFilter(req, resp);
24
25 }
26
27 public void destroy() {
28 }
29 }

web.xml файл при етом может выглядеть например так:

1 <?xml version="1.0" encoding="UTF-8"?>
2
3 <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
4 xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
5 xsi:schemaLocation=
"http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
6 version=
"2.4">

7
8 <display-name>MyApp</display-name>
9
10 <context-param>
11 <param-name>contextConfigLocation</param-name>
12 <param-value>
13 classpath:/**/applicationContext-*.xml,
14 </param-value>
15 </context-param>
16
17 <servlet>
18 <servlet-name>SpringMVC</servlet-name>
19 <servlet-class>
20 org.springframework.web.servlet.DispatcherServlet
21 </servlet-class>
22 <init-param>
23 <param-name>contextConfigLocation</param-name>
24 <param-value>classpath:**/SpringMVC-*.xml</param-value>
25 </init-param>
26 <load-on-startup>1</load-on-startup>
27 </servlet>
28
29 <context-param>
30 <param-name>log4jConfigLocation</param-name>
31 <param-value>/WEB-INF/classes/log4j.properties</param-value>
32 </context-param>
33
34 <filter>
35 <filter-name>MyFilter</filter-name>
36 <filter-class>mypack.MyFilter</filter-class>
37 </filter>
38 <filter-mapping>
39 <filter-name>MyFilter</filter-name>
40 <servlet-name>SpringMVC</servlet-name>
41 <dispatcher>REQUEST</dispatcher>
42 <dispatcher>FORWARD</dispatcher>
43 <dispatcher>INCLUDE</dispatcher>
44 </filter-mapping>
45
46 <servlet-mapping>
47 <servlet-name>SpringMVC</servlet-name>
48 <url-pattern>*.htm</url-pattern>
49 </servlet-mapping>
50
51 <welcome-file-list>
52 <welcome-file>index.html</welcome-file>
53 <welcome-file>index.jsp</welcome-file>
54 </welcome-file-list>
55
56 </web-app>