<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-757496094257544869</atom:id><lastBuildDate>Fri, 13 Jan 2012 08:11:39 +0000</lastBuildDate><category>logging</category><category>xml</category><category>socketio</category><category>hibernate</category><category>transaction</category><category>logout</category><category>instrumentation api</category><category>javascript</category><category>funny</category><category>login</category><category>cache</category><category>tool</category><category>bug</category><category>aop</category><category>spring security</category><category>maven</category><category>tomcat</category><category>event</category><category>benchmark</category><category>expression</category><category>applet</category><category>jvm</category><category>http</category><category>blazeds</category><category>spring javassist</category><category>flex</category><category>my project</category><category>bytecode</category><category>approach</category><category>sql</category><category>project managment</category><category>nginx</category><category>spring</category><category>browser</category><category>html</category><category>maven-plugin</category><category>liquibase</category><category>network</category><category>performance</category><category>thought</category><category>netty</category><category>database</category><category>tapestry</category><title>Java-Bulldozer</title><description>&lt;i&gt;"Nothing irks me more than opening a base class or interface and getting that ugly view of just the method names because source isn't available."&lt;/i&gt;
(c) Howard Lewis Ship</description><link>http://nkoksharov.blogspot.com/</link><managingEditor>noreply@blogger.com (Никита Кокшаров)</managingEditor><generator>Blogger</generator><openSearch:totalResults>42</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/nkoksharov" /><feedburner:info uri="nkoksharov" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-3452303531262953254</guid><pubDate>Thu, 12 Jan 2012 12:16:00 +0000</pubDate><atom:updated>2012-01-13T12:11:39.549+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">netty</category><category domain="http://www.blogger.com/atom/ns#">socketio</category><category domain="http://www.blogger.com/atom/ns#">network</category><title>Поддержка socket.io протокола на Java</title><description>&lt;p&gt;Опубликовал свой первый проект на github - &lt;a href="https://github.com/mrniko/netty-socketio"&gt;netty-socketio&lt;/a&gt;, реализующий серверную поддержку &lt;a href="http://socket.io"&gt;socket.io&lt;/a&gt; протокола на java. В основе использован легендарный фреймворк &lt;a href="http://netty.io"&gt;Netty&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Socket.io - библиотека предназначенная для создания постоянной связи браузера с сервером. Таким образом возможно организовывать доставку данных в реальном времени в обе стороны. Socket.io поддерживает несколько транспортных механизмов для организации такого взаимодействия - web-sockets, xhr-polling и т.д.&lt;/p&gt;&lt;p&gt;В моей реализации поддерживается &lt;a href="https://github.com/LearnBoost/socket.io-client"&gt;socket.io-client&lt;/a&gt; версии 0.8.7+. Из поддерживаемых транспортов пока только &lt;b&gt;xhr-polling&lt;/b&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-3452303531262953254?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/qN4A55CtjAA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/qN4A55CtjAA/socketio-netty.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2012/01/socketio-netty.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-8967670350129298476</guid><pubDate>Wed, 02 Mar 2011 15:00:00 +0000</pubDate><atom:updated>2011-03-02T19:33:14.928+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">xml</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Smooks: парсим xml с помощью ... xml</title><description>&lt;p&gt;&lt;a href="http://www.smooks.org/"&gt;Smooks&lt;/a&gt; - отличный инструмент, который решает задачу парсинга xml используя xml-маппинг тэгов и аттрибутов на объекты и их свойства. Вообще продукт позиционируется как инструмент для трансформации, биндинга, валидации и обработки данных в различных форматах (CSV, XML, EDI). Я рассматриваю этот продукт как достойную альтернативу других механизмов парсинга xml - JAXB, JAXP, Digester и пр.&lt;/p&gt;&lt;p&gt;Предположим нам надо распарсить такой вот xml:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;br /&gt;&lt;roles&gt;&lt;br /&gt; &lt;role id="1"&gt;&lt;br /&gt;  &lt;name&gt;Босс&lt;/name&gt;&lt;br /&gt;  &lt;bonusType&gt;ENERGY_SPEED&lt;/bonusType&gt;&lt;br /&gt;  &lt;bonuses&gt;&lt;br /&gt;   &lt;bonus level="1" percent="3"/&gt;&lt;br /&gt;  &lt;/bonuses&gt;&lt;br /&gt; &lt;/role&gt;&lt;br /&gt; &lt;role id="2"&gt;&lt;br /&gt;  &lt;name&gt;Менеджер&lt;/name&gt;&lt;br /&gt;  &lt;bonusType&gt;MAX_ENERGY&lt;/bonusType&gt;&lt;br /&gt;  &lt;bonuses&gt;&lt;br /&gt;   &lt;bonus level="1" percent="10"/&gt;&lt;br /&gt;   &lt;bonus level="2" percent="11"/&gt;&lt;br /&gt;   &lt;bonus level="3" percent="12"/&gt;&lt;br /&gt;  &lt;/bonuses&gt;&lt;br /&gt; &lt;/role&gt;&lt;br /&gt;&lt;/roles&gt;&lt;/script&gt;&lt;p&gt;Xml необходимо распарсить в List из объектов типа PlayerRole:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: java"&gt;&lt;br /&gt;public class PlayerRole {&lt;br /&gt;&lt;br /&gt;    public enum Bonus {ENERGY_SPEED, HEALTH_SPEED, MAX_ENERGY, MAX_HEALTH};&lt;br /&gt;&lt;br /&gt;    private Integer id;&lt;br /&gt;    private String name;&lt;br /&gt;    private Bonus bonusType;&lt;br /&gt;    // Map&lt;level, percent&gt;&lt;br /&gt;    private Map&lt;Integer, Double&gt; bonuses = new HashMap&lt;Integer, Double&gt;();&lt;br /&gt;&lt;br /&gt;    public String getName() {&lt;br /&gt;        return name;&lt;br /&gt;    }&lt;br /&gt;    public void setName(String name) {&lt;br /&gt;        this.name = name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Integer getCell() {&lt;br /&gt;        return cell;&lt;br /&gt;    }&lt;br /&gt;    public void setCell(Integer cell) {&lt;br /&gt;        this.cell = cell;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Bonus getBonusType() {&lt;br /&gt;        return bonusType;&lt;br /&gt;    }&lt;br /&gt;    public void setBonusType(Bonus bonusType) {&lt;br /&gt;        this.bonusType = bonusType;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Map&lt;Integer, Double&gt; getBonuses() {&lt;br /&gt;        return bonuses;&lt;br /&gt;    }&lt;br /&gt;    public void setBonuses(Map&lt;Integer, Double&gt; bonuses) {&lt;br /&gt;        this.bonuses = bonuses;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/script&gt;&lt;p&gt;Для начала создадим xml-маппинг необходимый для преобразования данных в объекты:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;?xml version="1.0"?&gt;&lt;br /&gt;&lt;smooks-resource-list&lt;br /&gt;    xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"&lt;br /&gt;    xmlns:g="http://www.milyn.org/xsd/smooks/groovy-1.1.xsd"&lt;br /&gt;    xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.3.xsd"&gt;&lt;br /&gt;&lt;br /&gt;    &lt;jb:bean beanId="roles-bean" class="java.util.ArrayList" createOnElement="roles"&gt;&lt;br /&gt;        &lt;jb:wiring beanIdRef="roles-role-bean" /&gt;&lt;br /&gt;    &lt;/jb:bean&gt;&lt;br /&gt;&lt;br /&gt;    &lt;jb:bean beanId="roles-role-bean" class="ru.somepackage.PlayerRole" createOnElement="role"&gt;&lt;br /&gt;        &lt;jb:value property="id" data="role @id"/&gt;&lt;br /&gt;        &lt;jb:value property="name" data="role/name"/&gt;&lt;br /&gt;        &lt;jb:value property="bonusType" data="role/bonusType"/&gt;&lt;br /&gt;        &lt;jb:wiring property="bonuses" beanIdRef="role-bonuses"/&gt;&lt;br /&gt;    &lt;/jb:bean&gt;&lt;br /&gt;&lt;br /&gt;    &lt;jb:bean beanId="role-bonuses" class="java.util.HashMap" createOnElement="bonuses"/&gt;&lt;br /&gt;&lt;br /&gt;    &lt;g:groovy executeOnElement="bonuses/bonus"&gt;&lt;br /&gt;        &lt;g:script&gt;&lt;br /&gt;            &lt;!--&lt;br /&gt;                use(DOMCategory) {&lt;br /&gt;                    def level = element.'@level';&lt;br /&gt;                    getBean('role-bonuses')[level as int] = (element.'@percent' as double) / 100;&lt;br /&gt;                }&lt;br /&gt;            --&gt;&lt;br /&gt;        &lt;/g:script&gt;&lt;br /&gt;    &lt;/g:groovy&gt;&lt;br /&gt;&lt;/smooks-resource-list&gt;&lt;/script&gt;&lt;p&gt;В Smooks все метаданные, необходимые для парсинга участков xml, представляются в виде бинов с сылками друг на друга. Адресация в xml осуществляется с помощью xpath-выражений. Если при парсинге необходима какая-то пост-обработка данных можно воспользоваться возможностью выполнения groovy-скриптов (тэг g:groovy), при этом код работы с xml будет более лаконичн чем на java. Из groovy также возможны обращение к бинам smooks.&lt;/p&gt;&lt;p&gt;Ну и наконец пример запуска механизма парсинга данных с приведенным конфигом:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: java"&gt;package ru.somepackage;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;&lt;br /&gt;import javax.xml.transform.Source;&lt;br /&gt;import javax.xml.transform.stream.StreamSource;&lt;br /&gt;&lt;br /&gt;import org.milyn.Smooks;&lt;br /&gt;import org.milyn.payload.JavaResult;&lt;br /&gt;&lt;br /&gt;public class SmooksLoader {&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        Smooks smooks = new Smooks();&lt;br /&gt;        InputStream configStream = SmooksLoader.class.getResourceAsStream("/config.xml");&lt;br /&gt;        smooks.addConfigurations(configStream);&lt;br /&gt;        JavaResult javaResult = new JavaResult();&lt;br /&gt;        try {&lt;br /&gt;            InputStream xmlStream = SmooksLoader.class.getResourceAsStream("/roles.xml");&lt;br /&gt;            Source source = new StreamSource(xmlStream);&lt;br /&gt;            smooks.filterSource(source, javaResult);&lt;br /&gt;        } catch (IOException e) {&lt;br /&gt;            throw new IllegalStateException(e);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        List&lt;PlayerRole&gt; roles = javaResult.getBean("roles-bean");&lt;br /&gt;        ....&lt;br /&gt;    }&lt;br /&gt;&lt;/script&gt;&lt;p&gt;Чтобы получить результат парсинга надо выбрать бин, обрабатывающий root-овый тэг, в нашем случае это бин с id="roles-bean".&lt;/p&gt;&lt;p&gt;Я выбраю этот движок для разбора xml т.к. он почти не требует кода и интегрирован с groovy. Использую его когда в проекте требуется распарсить различные конфигурационные ресурсы на этапе запуска сервера.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-8967670350129298476?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/m-XAPXrrJaM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/m-XAPXrrJaM/smooks-xml-xml.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2011/03/smooks-xml-xml.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-6431785157614517659</guid><pubDate>Fri, 14 Jan 2011 15:00:00 +0000</pubDate><atom:updated>2011-01-14T18:26:23.701+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">spring javassist</category><title>Использование Javassist для генерации прокси в Spring Framework</title><description>&lt;p&gt;Известно, что проект cglib давно находится в заброшенном состоянии, также при работе с ним возникают некоторые проблемы, описанные &lt;a href=""&gt;здесь&lt;/a&gt;. Наиболее популярная альтернатива этому проекту - &lt;a href="http://www.csg.is.titech.ac.jp/~chiba/javassist/"&gt;javassist&lt;/a&gt;. Он используется в таких проектах как JBoss AS, Hibernate, Weld ... &lt;/p&gt;&lt;p&gt;Только вот в Spring он еще не появился. Замена библиотеки cglib, используемой для генерации proxy-объектов, на javassist, судя по плану проекта, состоится в версии 3.1 проекта. Вот &lt;a href="https://jira.springframework.org/browse/SPR-5654"&gt;jira-таск&lt;/a&gt;. В этом же таске я прикрепил аттачи в виде трех классов и пары патчей, которые позволяют окончательно перейти на Javassist.&lt;/p&gt;&lt;p&gt;Для включения поддержки Javassist вам понадобится пристроить к себе в проект вот эти три класса - &lt;a href="https://jira.springframework.org/secure/attachment/17476/JavassistAopProxy.java"&gt;JavassistAopProxy&lt;/a&gt;, &lt;a href="https://jira.springframework.org/secure/attachment/17477/JavassistAopProxyFactory.java"&gt;JavassistAopProxyFactory&lt;/a&gt; и &lt;a href="https://jira.springframework.org/secure/attachment/17478/JavassistApplicationContext.java"&gt;JavassistApplicationContext&lt;/a&gt;. Затем вместо spring-овой реализации ApplicationContext-а задействовать &lt;code&gt;org.springframework.aop.framework.JavassistApplicationContext&lt;/code&gt;. Если вы не создаете его напрямую, а используете &lt;code&gt;org.springframework.web.context.ContextLoaderListener&lt;/code&gt; сконфигурированный в web.xml, то вам необоходимо будет добавить в этот же web.xml такие строки:&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: xml;"&gt;&lt;br /&gt;&lt;context-param&gt;&lt;br /&gt;   &lt;param-name&gt;contextClass&lt;/param-name&gt;&lt;br /&gt;   &lt;param-value&gt;org.springframework.aop.framework.JavassistApplicationContext&lt;/param-value&gt;&lt;br /&gt;&lt;/context-param&gt;&lt;/script&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-6431785157614517659?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/LI3Xsy6-8jw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/LI3Xsy6-8jw/javassist-spring-framework.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2011/01/javassist-spring-framework.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7153334418694153168</guid><pubDate>Sat, 06 Nov 2010 19:30:00 +0000</pubDate><atom:updated>2010-11-06T22:36:50.429+03:00</atom:updated><title>"Хорошая" новость от Oracle...</title><description>&lt;p&gt;Хотя это событие было предсказуемо, после коммерциализации MySQL и OpenOffice, верить в него не хотелось. Похоже oracle хочет разделить версии JVM на "free" и "premium", подробнее &lt;a href="http://www.theregister.co.uk/2010/11/06/oracle_dueling_jvms/"&gt;тут&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-7153334418694153168?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/RDkLb9J7UKI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/RDkLb9J7UKI/oracle.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>4</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2010/11/oracle.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-2560347677040750337</guid><pubDate>Wed, 03 Nov 2010 15:04:00 +0000</pubDate><atom:updated>2010-11-03T19:07:34.557+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Поиск по ключу входящему в числовой диапазон.</title><description>&lt;p&gt;Дана таблица по которой, в зависимости от опыта игрока, определяется его уровень:&lt;/p&gt;&lt;pre&gt;  Диапазон опыта     Уровень&lt;br /&gt;&lt;br /&gt;   0 &lt; exp &lt;= 12        1&lt;br /&gt;  12 &lt; exp &lt;= 30        2&lt;br /&gt;  30 &lt; exp &lt;= 67        3&lt;br /&gt;       ...             ...&lt;br /&gt;  231 &lt; exp &lt;= 300      10&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Задача: реализовать алгоритм поиска уровня.&lt;/p&gt;&lt;p&gt;Для решения задачи я задействую класс &lt;code&gt;java.util.TreeMap&lt;/code&gt;. Почему этот класс? Все просто, он реализует важный интерфейс - &lt;code&gt;java.util.NavigableMap&lt;/code&gt;, появившийся с версии Java 1.6. Его метод&lt;/p&gt;&lt;code&gt;Map.Entry&amp;lt;K,V&amp;gt; ceilingEntry(K key)&lt;/code&gt;&lt;p&gt;как раз позволяет найти запись в map-е удовлетворяющую условию K(n) &lt; key &lt;= K(n+1) и при этом будет содержать значение по ключу K(n+1). Таким образом, остается составить map-у из записей: ключ - макс. значение опыта на уровне, значение - уровень. Код решения:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;br /&gt;...&lt;br /&gt;private final NavigableMap&lt;Integer, Integer&gt; lMap = new TreeMap&lt;Integer, Integer&gt;();&lt;br /&gt;&lt;br /&gt;public void init() {&lt;br /&gt; lMap.put(12, 1);&lt;br /&gt; lMap.put(30, 2);&lt;br /&gt; lMap.put(67, 3);&lt;br /&gt; ...&lt;br /&gt; lMap.put(300, 10);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public Integer findLevel(Integer exp) {&lt;br /&gt;    return lMap.ceilingEntry(exp).getValue();&lt;br /&gt;}&lt;br /&gt;...&lt;/script&gt;&lt;p&gt;Для наглядности я захардкодил map-у в коде, на деле же она создается из xml.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-2560347677040750337?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/zjZIgUwjMQo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/zjZIgUwjMQo/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>7</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2010/11/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-8911987378485208755</guid><pubDate>Fri, 29 Oct 2010 18:57:00 +0000</pubDate><atom:updated>2010-11-03T23:38:46.176+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">performance</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Обновление максимального значения без использования блокировок</title><description>&lt;p&gt;При реализации грамотных многопоточных механизмов, порой используют только синхронизованные блоки кода. Но, не следует также забывать и о функции CAS (Compare and swap, представленной соответствующей процессорной командой). Данная функция присутствует во всех классах пакета &lt;code&gt;java.util.concurrent.atomic&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Алгортимы с использованием CAS являются безблокировочными, ведь потокам не приходится ожидать блокировок для их исполнения. Операция CAS выполняется успешно или неуспешно, но независимо от исхода она завершается в предсказуемые сроки. В случае неуспешности выполнения программа может сделать попытку выполнения этой функции, либо выполнить другие операции. &lt;/p&gt;&lt;p&gt;До недавнего времени примение CAS-у в своей практике я не находил. Однако, когда потребовалось реализовать многопоточный алгоритм обновления максимального значения, он оказался весьма уместен:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;br /&gt;final AtomicLong max = new AtomicLong(Long.MIN_VALUE);&lt;br /&gt;...&lt;br /&gt;public void updateMax(long val) {&lt;br /&gt;    while(true) {&lt;br /&gt;        long current = max.get();&lt;br /&gt;        if (val &lt; current&lt;br /&gt;              || max.compareAndSet(current, val)) {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;...&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-8911987378485208755?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/QI7gr4AxtYE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/QI7gr4AxtYE/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>3</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2010/10/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-881908433904661158</guid><pubDate>Sun, 08 Aug 2010 08:49:00 +0000</pubDate><atom:updated>2010-08-08T13:25:50.510+04:00</atom:updated><title>Реализация concurrent HashSet-а в Java</title><description>&lt;p&gt;К сожалению, в JDK нет полноценной реализации concurrent HashSet-а, как в случае для HashMap - &lt;code&gt;java.util.concurrent.ConcurrentHashMap&lt;/code&gt;. Есть &lt;code&gt;CopyOnWriteArraySet&lt;/code&gt;, использующий внутри себя &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;, но у него есть некоторые недостатки:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;поиск элементов будет медленне ибо в List-ах он выполняется по equals, а не по hashcode -&gt; equals.&lt;/li&gt;&lt;li&gt;невозможность удаления объектов при итерации (iterator.remove() - не реализован) и понятно почему, ведь при итерации используется копия коллекции.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Но выход есть, подобие ConcurrentHashSet можно сделать на основе ConcurrentHashMap, записывая как ключ элемент, а в значение какой-нибудь dummy-объект. Такая реализация представлена в виде &lt;code&gt;org.jboss.util.collection.ConcurrentSet&lt;/code&gt; из бибилиотеки &lt;a href="http://community.jboss.org/wiki/JBossCommonProject"&gt;jboss-common-core&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-881908433904661158?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/BlXUHhGVEKg" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/BlXUHhGVEKg/concurrent-hashset-java.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>5</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2010/08/concurrent-hashset-java.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-5079082816062964300</guid><pubDate>Tue, 06 Apr 2010 13:32:00 +0000</pubDate><atom:updated>2010-04-08T13:06:56.477+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">transaction</category><category domain="http://www.blogger.com/atom/ns#">spring</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Транзакционные синхронизации в Spring Framework</title><description>&lt;p&gt;Выполнение определенной логики по окончанию текущей транзакции в &lt;a href="http://www.springsource.org/"&gt;Spring Framework&lt;/a&gt; можно "подключать" динамически с помощью так называемых транзакционных синхронизаций.&lt;/p&gt;&lt;p&gt;Транзакционная синхронизация - это по сути callback-объект типа &lt;code&gt;org.springframework.transaction.support.TransactionSynchronization&lt;/code&gt;, который содержит в себе определенные методы вызываемые из AbstractPlatformTransactionManager на соответствующие действия над транзакцией (afterCompletion, afterCommit, afterRollback, suspend и т.д.). &lt;code&gt;org.springframework.transaction.support.TransactionSynchronizationManager&lt;/code&gt; - менеджер со статическими методами, используемый для регистрации и управления синхронизациями. При написании своей синхронизации в качестве базового класса удобно использовать &lt;code&gt;org.springframework.transaction.support.TransactionSynchronizationAdapter&lt;/code&gt;. Вот пример:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;br /&gt;...&lt;br /&gt;@Transactional&lt;br /&gt;public void someMethod() {&lt;br /&gt;      if (TransactionSynchronizationManager.isActualTransactionActive()) {&lt;br /&gt;          TransactionSynchronizationManager.registerSynchronization(&lt;br /&gt;                                  new TransactionSynchronizationAdapter() {&lt;br /&gt;              @Override&lt;br /&gt;              public void afterCompletion(int status) {&lt;br /&gt;                  // вызов этого куска кода произойдет &lt;br /&gt;                  // в результате любого окончания транзакции &lt;br /&gt;                  // rollback-а или commit-а&lt;br /&gt;              }&lt;br /&gt;          });&lt;br /&gt;      }&lt;br /&gt;      ...&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-5079082816062964300?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/fqTov6nXQCw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/fqTov6nXQCw/spring-framework.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>2</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2010/04/spring-framework.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-1512009880681305313</guid><pubDate>Fri, 05 Mar 2010 23:55:00 +0000</pubDate><atom:updated>2010-03-06T21:06:51.391+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">blazeds</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Сериализация полей без set-методов и поддержка аннотаций в BlazeDS</title><description>&lt;p&gt;Для передачи данных между &lt;a href="http://opensource.adobe.com/wiki/display/blazeds/BlazeDS/"&gt;BlazeDS 3.2&lt;/a&gt; и клиентом рекомендуется использовать value object-ы. Эти объекты содержат только те данные, которые действительно необходимы клиенту.&lt;/p&gt;&lt;p&gt;К сожалению, в BlazeDS есть некоторые неудобства при сериализации объектов:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Обязательное наличие set-метода (можно и пустого), если вы хотите сериализовать поле у которого есть только get-метод&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Сериализация enum-ов как объектов, вместо использования строчного значения, как заявлено в документации&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Так же не очень удобно, на мой взгляд, является создание отдельных классов для каждого value object-a. Было бы удобнее помечать аннтоциями getter-ы или поля, которые будут учавствовать/отсутсвовать в value object-е, без создания нового класса.&lt;/p&gt;&lt;p&gt;Чтобы устранить вышеперечисленные недостатки придется разобраться с механизмом сериализации BlazeDS. Кратко опишу классы, которые задействованы при сериализации: &lt;code&gt;flex.messaging.io.PropertyProxy&lt;/code&gt; - основной интерфейс, используемый для сериализации/десериализации объектов. Каждому классу объекта, передаваемому на клиента, соответствует определенная реализация &lt;code&gt;PropertyProxy&lt;/code&gt;, в основном это &lt;code&gt;flex.messaging.io.BeanProxy&lt;/code&gt;. Соответствие (Класс объекта - PropertyProxy-объект) держится в классе &lt;code&gt;flex.messaging.io.PropertyProxyRegistry&lt;/code&gt;, при сериализации/десериализации BlazeDS получает &lt;code&gt;PropertyProxy&lt;/code&gt; для какого-либо объекта используя методы: &lt;code&gt;getProxy&lt;/code&gt; и &lt;code&gt;getProxyAndRegister&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Т.к. &lt;code&gt;PropertyProxyRegistry&lt;/code&gt; является singleton-ом, то замену реализации нам придется сделать через смену значения поля &lt;code&gt;registry&lt;/code&gt;, которое спрятано внутри класса. Реализация, в которой убраны перечисленные выше недостатки, выглядит так:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;package com.blogspot.nkoksharov.blazeds;&lt;br /&gt;&lt;br /&gt;import java.beans.*;&lt;br /&gt;import java.lang.reflect.Method;&lt;br /&gt;import java.util.*;&lt;br /&gt;&lt;br /&gt;import javax.sql.RowSet;&lt;br /&gt;&lt;br /&gt;import org.springframework.flex.remoting.RemotingExclude;&lt;br /&gt;import org.springframework.beans.FatalBeanException;&lt;br /&gt;&lt;br /&gt;import java.lang.reflect.Field;&lt;br /&gt;&lt;br /&gt;import flex.messaging.MessageException;&lt;br /&gt;import flex.messaging.io.*;&lt;br /&gt;&lt;br /&gt;public class ExPropertyProxyRegistry extends PropertyProxyRegistry {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;    * Подмена реализации&lt;br /&gt;    */&lt;br /&gt;    static {&lt;br /&gt;        try {&lt;br /&gt;            Field field = PropertyProxyRegistry.class.getDeclaredField("registry");&lt;br /&gt;            field.setAccessible(true);&lt;br /&gt;            field.set(null, new ExPropertyProxyRegistry());&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            throw new FatalBeanException("Can't replace registry implementation", e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @SuppressWarnings("unchecked")&lt;br /&gt;    private PropertyProxy guessProxy(PropertyProxy proxy, Class clazz) {&lt;br /&gt;        if (proxy != null) {&lt;br /&gt;            return proxy;&lt;br /&gt;        }&lt;br /&gt;        PropertyProxy newProxy;&lt;br /&gt;&lt;br /&gt;        if (Map.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new MapProxy();&lt;br /&gt;        } else if (Throwable.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new ThrowableProxy();&lt;br /&gt;        } else if (PageableRowSet.class.isAssignableFrom(clazz) || RowSet.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new PageableRowSetProxy();&lt;br /&gt;        } else if (Dictionary.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new DictionaryProxy();&lt;br /&gt;        } else if (clazz.isEnum()) { // фикс сериализации enum-ов&lt;br /&gt;            return null;&lt;br /&gt;        } else {&lt;br /&gt;            newProxy = new BeanProxy();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        SerializationDescriptor descriptor = createDescriptor(clazz);&lt;br /&gt;        newProxy.setDescriptor(descriptor);&lt;br /&gt;        newProxy.setIncludeReadOnly(true); // фикс сериализации get-методов&lt;br /&gt;        return newProxy;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;    * Создаем дескриптор сериализации, который исключает из сериализации значения  &lt;br /&gt;    * get-методов аннтированных org.springframework.flex.remoting.RemotingExclude&lt;br /&gt;    */&lt;br /&gt;    private SerializationDescriptor createDescriptor(Class&lt;?&gt; clazz) {&lt;br /&gt;        List&lt;String&gt; excludeProps = new ArrayList&lt;String&gt;();&lt;br /&gt;        try {&lt;br /&gt;            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);&lt;br /&gt;            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();&lt;br /&gt;            for (PropertyDescriptor pd : pds) {&lt;br /&gt;                Method method = pd.getReadMethod();&lt;br /&gt;                if (method != null &amp;&amp; method.isAnnotationPresent(RemotingExclude.class)) {&lt;br /&gt;                    excludeProps.add(pd.getName());&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        } catch (IntrospectionException e) {&lt;br /&gt;            throw new MessageException(e);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        SerializationDescriptor descriptor = new SerializationDescriptor();&lt;br /&gt;        descriptor.setExcludes(excludeProps);&lt;br /&gt;        return descriptor;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    @SuppressWarnings("unchecked")&lt;br /&gt;    public PropertyProxy getProxy(Class clazz) {&lt;br /&gt;        PropertyProxy proxy = super.getProxy(clazz);&lt;br /&gt;        return guessProxy(proxy, clazz);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    @SuppressWarnings("unchecked")&lt;br /&gt;    public PropertyProxy getProxyAndRegister(Class clazz) {&lt;br /&gt;        PropertyProxy proxy = super.getProxyAndRegister(clazz);&lt;br /&gt;        return guessProxy(proxy, clazz);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-1512009880681305313?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/r61wTs9eKTc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/r61wTs9eKTc/set-blazeds.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/03/set-blazeds.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7250847433944451715</guid><pubDate>Tue, 29 Dec 2009 17:39:00 +0000</pubDate><atom:updated>2009-12-29T22:09:10.737+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">my project</category><title>Запуск "Наемники : Карты судьбы"</title><description>&lt;p&gt;Не так давно наступил день, который я так долго ждал. 25 декабря был запущен проект для ВКонтакте - игра &lt;b&gt;"Наемники : Карты судьбы"&lt;/b&gt;, над которым я трудился последние 2,5 месяца. Серверная часть проекта была реализована полностью мной, что называется "своими руками". Давненько мечтал о таком маленьком проекте, в котором я смогу применить все свои знания и опыт в области разработки.&lt;/p&gt;&lt;p&gt;Использовались следующие java-технологии:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Spring&lt;/li&gt;&lt;li&gt;Spring security&lt;/li&gt;&lt;li&gt;Spring BlazeDS Integration&lt;/li&gt;&lt;li&gt;BlazeDS&lt;/li&gt;&lt;li&gt;Hibernate&lt;/li&gt;&lt;li&gt;JBoss Cache&lt;/li&gt;&lt;li&gt;Unitils&lt;/li&gt;&lt;li&gt;Jmockit&lt;/li&gt;&lt;li&gt;DBUnit&lt;/li&gt;&lt;li&gt;TestNG&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Думаю, что некоторые из вас скажут, что с использованием Hibernate хорошей производительности не добьешься, но я с этим не соглашусь. Да, в процессе разработки в Hibernate нашлись некоторые баги и выявилось весьма "необычное" поведение, но все эти проблемы можно решить.&lt;/p&gt;&lt;p&gt;Теперь, когда проект запущен, я могу снова начинать писать статьи, освещая некоторые проблемы с которыми я столкнулся при разработке.&lt;/p&gt;&lt;p&gt;Отмечу также и тот факт, что более 80% кода было покрыто тестами. Хотя этот процесс и занимал у меня до 50% времени, я считаю, что именно благодаря тестам удалось выйти в релиз с минимальным кол-вом багов на серверной стороне. Без тестов был бы невозможен безболезненный рефакторинг и быстрое добавление новой функциональности.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-7250847433944451715?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/tXNuhrImwWw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/tXNuhrImwWw/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>14</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/12/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-6634198279393130409</guid><pubDate>Wed, 28 Oct 2009 12:24:00 +0000</pubDate><atom:updated>2010-01-12T17:09:10.959+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">spring</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Динамическая модификация контекста в Spring</title><description>&lt;p&gt;Существует механизм изменения контекста Spring, который запускается после сбора всей информации о нем (из xml и аннотаций) и срабатывает до создания самих bean-ов. Я говорю об интерфейсе &lt;code&gt;org.springframework.beans.factory.config.BeanFactoryPostProcessor&lt;/code&gt;, реализуя который вы получаете именно такую возможность. Используя данный механизм очень удобно менять параметры уже объявленных где-то в контексте bean-ов. Объект реализующий этот интерфейс можно зарегистрировать как обычный bean в контексте, либо добавить его на этапе создания контекста, например так:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: java"&gt;&lt;![CDATA[&lt;br /&gt;package com.blogspot.nkoksharov;&lt;br /&gt;&lt;br /&gt;import org.springframework.beans.*;&lt;br /&gt;import org.springframework.beans.factory.config.*;&lt;br /&gt;&lt;br /&gt;public class SpringTest {&lt;br /&gt;&lt;br /&gt;    public void testPostProcessor() {&lt;br /&gt;        ConfigurableApplicationContext context = &lt;br /&gt;             new ClassPathXmlApplicationContext(new String[] {"/spring/context.xml"}, false);&lt;br /&gt;        context.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {&lt;br /&gt;            @Override&lt;br /&gt;            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {&lt;br /&gt;                BeanDefinition definition = beanFactory.getBeanDefinition("sampleBean");&lt;br /&gt;                MutablePropertyValues values = definition.getPropertyValues();&lt;br /&gt;                MockObject object = new MockObject();&lt;br /&gt;                values.addPropertyValue("someProperty", object);&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        context.refresh();&lt;br /&gt;        &lt;br /&gt;        ... &lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}]]&gt;&lt;/script&gt;&lt;p&gt;Только не забудьте указать в конструкторе контекста аргументу &lt;code&gt;refresh&lt;/code&gt; значение &lt;code&gt;false&lt;/code&gt;, иначе наш механизм будет вызываться лишь для новых bean-ов добавляемых уж после создания контекста (как это делать я писал &lt;a href="http://nkoksharov.blogspot.com/2009/02/bean-spring.html"&gt;тут&lt;/a&gt;). При вызове метода &lt;code&gt;refresh&lt;/code&gt; произойдет инициализация контекста и наш обработчик будет вызван.&lt;/p&gt;&lt;p&gt;Описанный выше прием оказывается очень полезен в использовании при тестировании, когда нужно изменить какие-то из параметров bean-ов или просто выставить свои значения.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-6634198279393130409?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/U8N7eV7gAR0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/U8N7eV7gAR0/spring.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/10/spring.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-5049506678846792594</guid><pubDate>Mon, 17 Aug 2009 13:17:00 +0000</pubDate><atom:updated>2009-10-28T15:41:40.220+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">maven-plugin</category><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">maven</category><title>Управление версиями в Maven</title><description>&lt;p&gt;После очередного релиза проекта приходится менять его версию, проходясь при этом по всем pom-файлам. Процедура эта довольно утомительная и избавить от нее поможет &lt;a href="http://mojo.codehaus.org/versions-maven-plugin/"&gt;versions-maven-plugin&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Достаточно выставить в головном pom-файле новую версию проекта и затем выполнить команду:&lt;/p&gt;&lt;code&gt;mvn versions:update-child-modules&lt;/code&gt;&lt;p&gt;Как вы наверное догадались, команда выполнит обновление всех версий проекта в pom-файлах дочерних модулей. Если же, по какой-то причине, вы остались недовольны проделанной работой плагина, то содержимое pom-файлов можно легко вернуть в исходное состояние командой:&lt;/p&gt;&lt;code&gt;mvn versions:revert&lt;/code&gt;&lt;p&gt;Чтобы удалить backup-ы pom-файлов выполните:&lt;/p&gt;&lt;code&gt;mvn versions:commit&lt;/code&gt;&lt;p&gt;Помимо выполнения обновления версии проекта, есть еще несколько полезных возможностей. Вывод списка новых версий зависимостей проекта, доступных из репозитария:&lt;/p&gt;&lt;code&gt;mvn versions:display-dependency-updates&lt;/code&gt;&lt;p&gt;Список новых версий плагинов используемых в проекте:&lt;/p&gt;&lt;code&gt;mvn versions:display-plugin-updates&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-5049506678846792594?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/WdOruCum_7U" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/WdOruCum_7U/maven.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>5</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/08/maven.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-4331550303110327883</guid><pubDate>Sat, 08 Aug 2009 12:43:00 +0000</pubDate><atom:updated>2009-08-09T16:50:39.103+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">tapestry</category><category domain="http://www.blogger.com/atom/ns#">performance</category><category domain="http://www.blogger.com/atom/ns#">nginx</category><title>Параметры производительности nginx и tapestry 5</title><description>&lt;p&gt;Думаю, что вам известно о HTTP-сервере &lt;a href="http://sysoev.ru/nginx/"&gt;nginx&lt;/a&gt;, а также возможности его использования в качестве front-end сервера для передачи разных ресурсов клиенту. Использование nginx позволяет разгрузить ваш сервер Java-приложений от лишней работы со стататическими ресурсами.&lt;/p&gt;&lt;p&gt;Отмечу лишь несколько основных, на мой взгляд параметров, влияющих на производительность системы использующей Nginx и веб-фреймворка &lt;a href="http://tapestry.apache.org"&gt;Tapestry 5&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Параметры для nginx, выставляемые в nginx.conf:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;worker_processes&lt;/code&gt; - кол-во процессов, обеспечивающих загрузку ресурсов с диска. Значение зависит от объема ресурсов и скорости носителя с которого производится чтение. Также играет роль число ядер/процессоров в вашем сервере, думаю, что значение должно быть не меньше чем их кол-во;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;worker_connections&lt;/code&gt; - максимальное кол-во одновременных соединений с сервером. Следует выбрать оптимальное число, которое сможет держать сам сервер-приложений. Именно верный порог соединений предотвратит DDOS-атаки на ваш сервер и даст уверенность в том, что ваш сервер будет устойчиво работать.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Настройки производительности у Tapestry 5 сводятся к верной настройке пула страниц. Напомню, что при каждом обращении клиента фреймворк вытаскивает из пула экземпляр определенной страницы. Параметры:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;tapestry.page-pool.hard-limit&lt;/code&gt; - максимальное значение страниц в пуле. Следует учесть, количество локалей поддерживаемых вашим приложением, т.к. для каждой локали создается отдельный экземпляр страницы (по-умолчанию значение 20);&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;tapestry.page-pool.soft-limit&lt;/code&gt; - кол-во страниц в пуле, после которого tapestry перейдет в ожидание освободившихся страниц. Пока число страниц в пуле не достигнет этого значения и свободных экземпляров не будет, они будут создаваться (по-умолчанию значение 5).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-4331550303110327883?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/eUZ_OamePkE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/eUZ_OamePkE/nginx-tapestry-5.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>4</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/08/nginx-tapestry-5.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-1308552199719325887</guid><pubDate>Mon, 03 Aug 2009 15:30:00 +0000</pubDate><atom:updated>2010-01-12T17:22:13.629+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">maven-plugin</category><category domain="http://www.blogger.com/atom/ns#">hibernate</category><category domain="http://www.blogger.com/atom/ns#">liquibase</category><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">database</category><title>Использование LiquiBase с Hibernate и Maven. Часть 2</title><description>&lt;p&gt;Теперь о том, что касается изменений которые вносит hibernate в структуру БД, при добавлении/изменении сущностей, если в настройках указан параметр &lt;code&gt;hibernate.hbm2ddl.auto=update&lt;/code&gt;. Как продолжить пользоваться этой возможностью, если теперь для всех изменений структуры БД мы должны использовать только liquibase? Было бы не плохо получать sql генерируемый hibernate, который можно легко поместить в changeset liquibase. Для этого необходимо подключить &lt;a href="http://mojo.codehaus.org/maven-hibernate3/hibernate3-maven-plugin/"&gt;hibernate3-maven-plugin&lt;/a&gt; к модулю, в котором используется hibernate:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;properties&gt;&lt;br /&gt;      &lt;hibernate.update&gt;false&lt;/hibernate.update&gt;&lt;br /&gt;      &lt;hibernate.create&gt;false&lt;/hibernate.create&gt;&lt;br /&gt;&lt;/properties&gt;&lt;br /&gt;&lt;build&gt;&lt;br /&gt;      &lt;plugins&gt;&lt;br /&gt;          ...&lt;br /&gt;          &lt;plugin&gt;&lt;br /&gt;            &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;&lt;br /&gt;            &lt;artifactId&gt;hibernate3-maven-plugin&lt;/artifactId&gt;&lt;br /&gt;            &lt;version&gt;2.2&lt;/version&gt;&lt;br /&gt;            &lt;configuration&gt;&lt;br /&gt;              &lt;componentProperties&gt;&lt;br /&gt;                &lt;outputfilename&gt;schema.sql&lt;/outputfilename&gt;&lt;br /&gt;                &lt;configurationfile&gt;/resources/hibernate.cfg.xml&lt;/configurationfile&gt;&lt;br /&gt;                &lt;export&gt;false&lt;/export&gt;&lt;br /&gt;                &lt;update&gt;${hibernate.update}&lt;/update&gt;&lt;br /&gt;                &lt;create&gt;${hibernate.create}&lt;/create&gt;&lt;br /&gt;                &lt;drop&gt;true&lt;/drop&gt;&lt;br /&gt;              &lt;/componentProperties&gt;&lt;br /&gt;            &lt;/configuration&gt;&lt;br /&gt;            &lt;dependencies&gt;&lt;br /&gt;                &lt;dependency&gt;&lt;br /&gt;                    &lt;groupId&gt;postgresql&lt;/groupId&gt;&lt;br /&gt;                    &lt;artifactId&gt;postgresql&lt;/artifactId&gt;&lt;br /&gt;                    &lt;version&gt;8.3-603.jdbc4&lt;/version&gt;&lt;br /&gt;                &lt;/dependency&gt;&lt;br /&gt;            &lt;/dependencies&gt;&lt;br /&gt;          &lt;/plugin&gt;&lt;br /&gt;      &lt;/plugins&gt;&lt;br /&gt;&lt;/build&gt;&lt;/script&gt;&lt;p&gt;Необоходимым условием для работы плагина является наличие файла &lt;code&gt;hibernate.hbm.xml&lt;/code&gt;, в нем должны содержаться следующие настройки:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;hibernate-configuration&gt;&lt;br /&gt;    &lt;session-factory&gt;&lt;br /&gt;        &lt;property name="hibernate.connection.driver_class"&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name="hibernate.connection.url"&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name="hibernate.connection.username"&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name="hibernate.connection.password"&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name="hibernate.dialect"&gt;...&lt;/property&gt;&lt;br /&gt;&lt;br /&gt;        &lt;mapping class="ru.somesite.SomeObject"/&gt;&lt;br /&gt;        ...&lt;br /&gt;    &lt;/session-factory&gt;&lt;br /&gt;&lt;/hibernate-configuration&gt;&lt;/script&gt;&lt;p&gt;Не забудьте также и о том, что теперь все маппинги классов тоже нужно указывать в этом файле, а не в каком-нибудь SessionFactoryBean, если используете Spring. Настройка SessionFactoryBean будет выглядеть примерно так:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;bean id="sessionFactory"&lt;br /&gt;        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&gt;&lt;br /&gt;        &lt;property name="dataSource" ref="dataSource" /&gt;&lt;br /&gt;        &lt;property name="configLocation" value="classpath:/hibernate.cfg.xml" /&gt;&lt;br /&gt;        &lt;property name="hibernateProperties"&gt;&lt;br /&gt;            &lt;props&gt;&lt;br /&gt;                ...&lt;br /&gt;                &lt;prop key="hibernate.hbm2ddl.auto"&gt;validate&lt;/prop&gt;&lt;br /&gt;            &lt;/props&gt;&lt;br /&gt;        &lt;/property&gt;&lt;br /&gt;&lt;/bean&gt;&lt;/script&gt;&lt;p&gt;Стоит отметить, что параметру &lt;code&gt;hibernate.hbm2ddl.auto&lt;/code&gt;, в вашем проекте, нужно присвоить значение &lt;code&gt;validate&lt;/code&gt;. Теперь при каждом выполнении команды:&lt;/p&gt;&lt;pre&gt;mvn hibernate3:hbm2ddl -Dhibernate.create=true&lt;/pre&gt;&lt;p&gt;вам будет выводится sql генерируемый hibernate, который можно вставить в changeset. Параметр &lt;code&gt;-Dhibernate.create=true&lt;/code&gt; следует указывать, в том случае, когда вам требуется SQL всей схемы БД. В случае, когда требуется получить SQL-команды, выполняемые hibernate для обновления схемы БД, то вместо первого параметра используйте &lt;code&gt;-Dhibernate.update=true&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;И помните, что в случае обновления БД вам нужно будет следить SQL, генерируемым hibernate, т.к. не все изменения параметров объектов входят в SQL (к примеру, hibernate не будет создавать некоторые constraint-ы).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-1308552199719325887?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/AwZs59Dnqa4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/AwZs59Dnqa4/liquibase-hibernate-maven-2.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/07/liquibase-hibernate-maven-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7105586250414106802</guid><pubDate>Thu, 09 Jul 2009 18:00:00 +0000</pubDate><atom:updated>2010-01-12T17:31:17.825+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">maven-plugin</category><category domain="http://www.blogger.com/atom/ns#">hibernate</category><category domain="http://www.blogger.com/atom/ns#">liquibase</category><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">database</category><title>Использование LiquiBase с Hibernate и Maven. Часть 1</title><description>&lt;p&gt;Когда-то я представлял вашему вниманию инструмент для управления изменениями в БД - &lt;a href="http://liquibase.org/"&gt;liquibase&lt;/a&gt;. Теперь я решил описать как его можно использовать в проектах с &lt;a href="http://maven.apache.org/"&gt;maven&lt;/a&gt;-сборкой, и &lt;a href="http://www.hibernate.org/"&gt;hibernate&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Для начала необходимо создать отдельный maven-артефакт, который будет содержать только ресурсы - xml-файлы с changeSet-ами от liquibase. Структура файлов в папке &lt;code&gt;resources&lt;/code&gt; нашего артефакта следующая:&lt;/p&gt;&lt;pre&gt;    /changelogs/версия_проекта/*-changelog.xml&lt;br /&gt;    /changelogs/версия_проекта/release-changelog.xml&lt;br /&gt;    /project-changelogs.xml&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Файл &lt;code&gt;project-changesets.xml&lt;/code&gt; включает в себя набор файлов &lt;code&gt;release-changelog.xml&lt;/code&gt; всех версий проекта, вот пример его содержимого:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;databaseChangeLog&lt;br /&gt;        xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"&lt;br /&gt;        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9 &lt;br /&gt;                http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd"&gt;&lt;br /&gt;&lt;br /&gt;     &lt;include file="changelogs/1.0/release-changelog.xml" relativeToChangelogFile="true"/&gt;&lt;br /&gt;     &lt;include file="changelogs/1.1/release-changelog.xml" relativeToChangelogFile="true"/&gt;&lt;br /&gt;     &lt;include file="changelogs/2.0/release-changelog.xml" relativeToChangelogFile="true"/&gt;&lt;br /&gt;     ...&lt;br /&gt;&lt;/databaseChangeLog&gt;&lt;/script&gt;&lt;p&gt;Таким же образом файлы &lt;code&gt;release-changelog.xml&lt;/code&gt; включают в себя список файлов с измененями, находящихся в той же папке и касающихся лишь данной версии проекта.  Сами изменения я предпочитаю описывать не пользуясь при это тэгами liquibase, а использую привычный SQL.&lt;/p&gt;&lt;p&gt;Благодаря наличию в liquibase такого понятия как "контекст", можно разделить все операции над БД на три контекста:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;schemedata&lt;/code&gt; - контекст в котором будут отражены изменения структруры базы;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;initdata&lt;/code&gt; - контекст определяющий данные для инициализации БД, например добавление новых пользовательских ролей;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;migratedata&lt;/code&gt; - контекст используемый при изменении "живых" данных, т.е данных появляющихся в системе при ее эксплуатации.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Такая концепция позволит вам четко разделять виды изменений в БД. А также "накатывать" изменения лишь нужных контекстов. Пример:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;changeSet id="2" author="nkoksharov" context="initdata"&gt;&lt;br /&gt;        &lt;sql&gt;&lt;br /&gt;           insert into roles values (123, 'ROLE_USER');&lt;br /&gt;        &lt;/sql&gt;&lt;br /&gt;        &lt;rollback&gt;&lt;br /&gt;            &lt;sql&gt;&lt;br /&gt;               delete from roles where id = 123;&lt;br /&gt;            &lt;/sql&gt;&lt;br /&gt;        &lt;/rollback&gt;&lt;br /&gt;&lt;/changeSet&gt;&lt;/script&gt;&lt;p&gt;Если в проекте предполагается использовать две базы - БД разработчика/продакшн и БД для тестов, то pom-файл, созданного нами артефакта, будет содержать следующие строки:&lt;/p&gt;&lt;script type="syntaxhighlighter" class="brush: xml"&gt;&lt;project&gt;&lt;br /&gt;    &lt;properties&gt;&lt;br /&gt;        &lt;liquibase.goal&gt;update&lt;/liquibase.goal&gt;&lt;br /&gt;    &lt;/properties&gt;&lt;br /&gt;    &lt;profiles&gt;&lt;br /&gt;        &lt;profile&gt;&lt;br /&gt;            &lt;id&gt;devdb&lt;/id&gt;&lt;br /&gt;            &lt;activation&gt;&lt;br /&gt;                &lt;activeByDefault&gt;true&lt;/activeByDefault&gt;&lt;br /&gt;            &lt;/activation&gt;&lt;br /&gt;            &lt;properties&gt;&lt;br /&gt;                &lt;database.url&gt;${app.database.url}&lt;/database.url&gt;&lt;br /&gt;                &lt;database.user&gt;${app.database.user}&lt;/database.user&gt;&lt;br /&gt;                &lt;database.password&gt;${app.database.password}&lt;br /&gt;                &lt;/database.password&gt;&lt;br /&gt;                &lt;database.contexts&gt;${app.database.contexts}&lt;br /&gt;                &lt;/database.contexts&gt;&lt;br /&gt;            &lt;/properties&gt;&lt;br /&gt;        &lt;/profile&gt;&lt;br /&gt;        &lt;profile&gt;&lt;br /&gt;            &lt;id&gt;testdb&lt;/id&gt;&lt;br /&gt;            &lt;properties&gt;&lt;br /&gt;                &lt;database.url&gt;${test.database.url}&lt;/database.url&gt;&lt;br /&gt;                &lt;database.user&gt;${test.database.user}&lt;/database.user&gt;&lt;br /&gt;                &lt;database.password&gt;${test.database.password}&lt;br /&gt;                &lt;/database.password&gt;&lt;br /&gt;                &lt;database.contexts&gt;${test.database.contexts}&lt;br /&gt;                &lt;/database.contexts&gt;&lt;br /&gt;            &lt;/properties&gt;&lt;br /&gt;        &lt;/profile&gt;&lt;br /&gt;    &lt;/profiles&gt;&lt;br /&gt;&lt;br /&gt;    &lt;build&gt;&lt;br /&gt;        &lt;plugins&gt;&lt;br /&gt;            &lt;plugin&gt;&lt;br /&gt;                &lt;groupId&gt;org.liquibase&lt;/groupId&gt;&lt;br /&gt;                &lt;artifactId&gt;liquibase-plugin&lt;/artifactId&gt;&lt;br /&gt;                &lt;version&gt;1.9.3.0&lt;/version&gt;&lt;br /&gt;                &lt;configuration&gt;&lt;br /&gt;                    &lt;changeLogFile&gt;src/main/resources/project-changelogs.xml&lt;br /&gt;                    &lt;/changeLogFile&gt;&lt;br /&gt;                    &lt;driver&gt;org.postgresql.Driver&lt;/driver&gt;&lt;br /&gt;&lt;br /&gt;                    &lt;url&gt;${database.url}&lt;/url&gt;&lt;br /&gt;                    &lt;username&gt;${database.user}&lt;/username&gt;&lt;br /&gt;                    &lt;password&gt;${database.password}&lt;/password&gt;&lt;br /&gt;                    &lt;contexts&gt;${database.contexts}&lt;/contexts&gt;&lt;br /&gt;                &lt;/configuration&gt;&lt;br /&gt;                &lt;executions&gt;&lt;br /&gt;                    &lt;execution&gt;&lt;br /&gt;                        &lt;id&gt;appdb&lt;/id&gt;&lt;br /&gt;                        &lt;phase&gt;process-resources&lt;/phase&gt;&lt;br /&gt;                        &lt;configuration&gt;&lt;br /&gt;                            &lt;contexts&gt;${app.database.contexts}&lt;br /&gt;                            &lt;/contexts&gt;&lt;br /&gt;                            &lt;url&gt;${app.database.url}&lt;/url&gt;&lt;br /&gt;                            &lt;username&gt;${app.database.user}&lt;/username&gt;&lt;br /&gt;                            &lt;password&gt;${app.database.password}&lt;br /&gt;                            &lt;/password&gt;&lt;br /&gt;                        &lt;/configuration&gt;&lt;br /&gt;                        &lt;goals&gt;&lt;br /&gt;                            &lt;goal&gt;${liquibase.goal}&lt;/goal&gt;&lt;br /&gt;                        &lt;/goals&gt;&lt;br /&gt;                    &lt;/execution&gt;&lt;br /&gt;                    &lt;execution&gt;&lt;br /&gt;                        &lt;id&gt;testdb&lt;/id&gt;&lt;br /&gt;                        &lt;phase&gt;process-resources&lt;/phase&gt;&lt;br /&gt;                        &lt;configuration&gt;&lt;br /&gt;                            &lt;contexts&gt;${test.database.contexts}&lt;br /&gt;                            &lt;/contexts&gt;&lt;br /&gt;                            &lt;url&gt;${test.database.url}&lt;/url&gt;&lt;br /&gt;                            &lt;username&gt;${test.database.user}&lt;/username&gt;&lt;br /&gt;                            &lt;password&gt;${test.database.password}&lt;br /&gt;                            &lt;/password&gt;&lt;br /&gt;                        &lt;/configuration&gt;&lt;br /&gt;                        &lt;goals&gt;&lt;br /&gt;                            &lt;goal&gt;${liquibase.goal}&lt;/goal&gt;&lt;br /&gt;                        &lt;/goals&gt;&lt;br /&gt;                    &lt;/execution&gt;&lt;br /&gt;                &lt;/executions&gt;&lt;br /&gt;            &lt;/plugin&gt;&lt;br /&gt;        &lt;/plugins&gt;&lt;br /&gt;    &lt;/build&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;/project&gt;&lt;/script&gt;&lt;p&gt;Параметры &lt;code&gt;app.database.xxx&lt;/code&gt; и &lt;code&gt;test.database.xxx&lt;/code&gt; можно определить в головном pom-файле проекта. Теперь накатывать изменения на обе БД можно одной командой:&lt;/p&gt;&lt;p&gt;&lt;code&gt;mvn process-resources&lt;/code&gt; либо &lt;code&gt;mvn clean install&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Чтобы сделать rollback обеих баз на несколько changeset-ов назад к команде следует добавить параметры:&lt;/p&gt;&lt;pre&gt;-Dliquibase.goal=rollback -Dliquibase.rollbackCount=1&lt;/pre&gt;&lt;p&gt;&lt;code&gt;liquibase.rollbackCount&lt;/code&gt; - определят кол-во &lt;code&gt;changeset&lt;/code&gt;-ов которые следует откатить.&lt;/p&gt;&lt;p&gt;Порой может возникнуть ситуация, когда требуется внести изменение в существующий changeset и заново накатить все changeset-и. Здесь поможет параметр:&lt;/p&gt;&lt;pre&gt;-Dliquibase.dropFirst=true&lt;/pre&gt;&lt;p&gt;Если потребуется работать с каждой БД отдельно, то необходимо использовать уже команды самого плагина liquibase:&lt;/p&gt;&lt;p&gt;&lt;code&gt;mvn liquibase:update&lt;/code&gt; либо &lt;code&gt;mvn liquibase:rollback&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Для тестовой БД добавляем параметр &lt;code&gt;-Ptestdb&lt;/code&gt; в командной строке.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-7105586250414106802?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/7I_LanXH-0A" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/7I_LanXH-0A/liquibase-hibernate-maven-1.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/07/liquibase-hibernate-maven-1.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-1106612128410406894</guid><pubDate>Sat, 02 May 2009 13:45:00 +0000</pubDate><atom:updated>2009-05-02T18:45:09.474+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">tapestry</category><category domain="http://www.blogger.com/atom/ns#">javascript</category><category domain="http://www.blogger.com/atom/ns#">aop</category><title>Javascript AOP</title><description>&lt;p&gt;Наверное каждому известно что такое АОП или по-просту аспектно-ориентированное программирование. В java есть целый ряд фреймворков реализующих определенные возможности данного подхода.&lt;/p&gt;&lt;p&gt;Но если смотреть в сторону javascript-языка, то возможности АОП в нем реализовать довольно просто:&lt;/p&gt;&lt;pre&gt;var AOPUtils = {&lt;br /&gt;    addBefore: function(object, methodName, invokedObject, invokedMethodName) {&lt;br /&gt;        var oldMethod = object[methodName];&lt;br /&gt;        object[methodName] = function() {&lt;br /&gt;          var args = $A(arguments);&lt;br /&gt;          try {&lt;br /&gt;            if (Object.isFunction(invokedObject)) {&lt;br /&gt;              invokedObject.call(invokedObject, args);&lt;br /&gt;            } else {&lt;br /&gt;              invokedObject[invokedMethodName].call(invokedObject, args);&lt;br /&gt;            }&lt;br /&gt;          } catch (e) {&lt;br /&gt;            alert("An exception occurred in method '" + methodName + "' Error: " + e.message);&lt;br /&gt;          }&lt;br /&gt;          try {&lt;br /&gt;            var result = oldMethod.apply(object, args);&lt;br /&gt;          } catch (e) {&lt;br /&gt;            alert("An exception occurred in method '" + methodName + "' Error: " + e.message);&lt;br /&gt;          }&lt;br /&gt;          return result;&lt;br /&gt;        };&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Представленная функция &lt;code&gt;AOPUtils.addBefore&lt;/code&gt; позволяет вам выполнить свою функцию перед функцией какого-либо объекта. Для реализации использовались стандартные javascript-функции, смысл которых состоит в следующем: &lt;code&gt;apply&lt;/code&gt; - вызов функции с параметрами в виде массива, &lt;code&gt;call&lt;/code&gt; - вызов функции с перечислением параметров через запятую. Первым аргументом обеих функций будет объект который в вызываемой функции будет представлять ссылку на this.&lt;/p&gt;&lt;p&gt;Сначала произойдет вызов вашей функции, затем функции на которую мы назначили данный аспект. Причем, в первую функцию аргументы перехваченного вызова будут переданы в виде массива, а во вторую обычным способом. Это сделано для того, чтобы наша функция, перехватывающая вызов, имела возможность переопределить значения параметров, которые идут вызываемой функции. Пример использования:&lt;/p&gt;&lt;pre&gt;var SomeClass = Class.create({&lt;br /&gt;    simplemethod: function(url) {&lt;br /&gt;        alert(url);&lt;br /&gt;    }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;var sobject = new SomeClass();&lt;br /&gt;AOPUtils.addBefore(sobject, "simplemethod", function(args) {&lt;br /&gt;    var url = args[0];&lt;br /&gt;    url += "?param=test";&lt;br /&gt;    args[0] = url;&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;sobject.simplemethod("http://test.com");&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Отмечу, что для использования данного механизма необоходма библиотека &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-1106612128410406894?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/Nd1Dr1807F8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/Nd1Dr1807F8/javascript-aop.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/05/javascript-aop.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7860589182708614623</guid><pubDate>Wed, 01 Apr 2009 20:16:00 +0000</pubDate><atom:updated>2009-04-02T10:42:47.371+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">tomcat</category><title>Переключение из HTTPS-соединения в HTTP (в Tomcat)</title><description>&lt;p&gt;Для наиболее безопасной передачи конфиденциальных данных по сети, в веб-приложениях, используется HTTPS-соединение. HTTPS-соединение лучше использовать  не на весь пользовательский сеанс, а лишь на определенных этапах где защищенность канала действительно очень важна.&lt;/p&gt;&lt;p&gt;К примеру, если сделать на HTTPS-соединении только логин пользователя, а после его входа в систему снова использовать обычный HTTP, то на web-сервере Tomcat придется реализовать поддержку корректного переключения с HTTPS на HTTP.&lt;/p&gt;&lt;p&gt;Конфигурация Spring Security для такого случая:&lt;/p&gt;&lt;pre&gt;&amp;lt;security:http auto-config=&amp;quot;true&amp;quot; &amp;gt;&lt;br /&gt;   &amp;lt;security:intercept-url pattern=&amp;quot;/login.html&amp;quot; access=&amp;quot;IS_AUTHENTICATED_ANONYMOUSLY&amp;quot; &lt;br /&gt;       requires-channel=&amp;quot;https&amp;quot;/&amp;gt;&lt;br /&gt;   &amp;lt;security:intercept-url pattern=&amp;quot;/billing.html&amp;quot; access=&amp;quot;ROLE_USER&amp;quot; &lt;br /&gt;       requires-channel=&amp;quot;http&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/security:http&amp;gt;&lt;/pre&gt;&lt;p&gt;Возможны два варианта переключения соединений:&lt;/p&gt;&lt;br /&gt;&lt;div style="text-align:center;"&gt;&lt;span style="color:red;"&gt;1.&lt;/span&gt;&lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/aEWqVghjGr3PKieJe5afGb/download/bean31.gif"/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style="color:red;"&gt;2.&lt;/span&gt;&lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/aITfaWhjGr3RI_eJe5afGb/download/bean32.gif"/&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;В первом случае все будет работать, т.к. Tomcat создаст cookie сперва для HTTP сессии, которая будет действительна и для HTTPS, а следовательно может быть аутентифицирована Spring Security и использована дальше в работе с обычным HTTP.&lt;/p&gt;&lt;p&gt;Во втором случае Tomcat создаст cookie для HTTPS с параметром secure, а это значит что аутентификация такой сессии не позволит использовать в дальнейшем обычную HTTP сессию. Сookie с параметром secure привязана лишь к HTTPS-сессиям. Для пользователя же это обернется редиректом Spring Security на страницу логина, но с уже созданной cookie, и только второй логин уже пустит пользователя в систему. Чтобы решить эту проблему необходимо, находясь в HTTPS сессии, создать cookie для обычной HTTP сессии без параметра secure. С помощью фильтра это реализовать довольно просто:&lt;/p&gt;&lt;pre&gt;package com.blogspot.nkoksharov.filter;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;&lt;br /&gt;import javax.servlet.*;&lt;br /&gt;import javax.servlet.http.*;&lt;br /&gt;&lt;br /&gt;import org.apache.commons.lang.StringUtils;&lt;br /&gt;&lt;br /&gt;public class HttpsCookieFilter implements Filter {&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void doFilter(ServletRequest request, ServletResponse response,&lt;br /&gt;            FilterChain chain) throws IOException, ServletException {&lt;br /&gt;        final HttpServletRequest httpRequest = (HttpServletRequest) request;&lt;br /&gt;        final HttpServletResponse httpResponse = (HttpServletResponse) response;&lt;br /&gt;        final HttpSession session = httpRequest.getSession(false);&lt;br /&gt;&lt;br /&gt;        if (request.isSecure() &amp;&amp; session != null) {&lt;br /&gt;            final Cookie sessionCookie = new Cookie("JSESSIONID", session.getId());&lt;br /&gt;            sessionCookie.setMaxAge(-1);&lt;br /&gt;            sessionCookie.setSecure(false);&lt;br /&gt;            String contextPath = httpRequest.getContextPath();&lt;br /&gt;            if (StringUtils.isNotBlank(contextPath)) {&lt;br /&gt;                sessionCookie.setPath(contextPath);&lt;br /&gt;            } else {&lt;br /&gt;                sessionCookie.setPath("/");&lt;br /&gt;            }&lt;br /&gt;            httpResponse.addCookie(sessionCookie);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        chain.doFilter(request, response);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Теперь переход с https-соединения будет выглядеть так:&lt;/p&gt;&lt;div style="text-align:center;"&gt;&lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/aM7oOShjGr3RTLeJe5aVNr/download/bean33.gif"/&gt;&lt;/div&gt;&lt;p&gt;Фильтр лучше всего поместить в самом начале цепочки фильтров, важно чтобы он был перед &lt;code&gt;springSecurityFilterChain&lt;/code&gt;. Следует отметить, что при таком решении http-cookie будет приходить пользователю на каждый https-запрос. Т.к. сложно опеределить при https-соединении создана ли cookie для http-канала.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-7860589182708614623?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/Ix60MdsAHok" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/Ix60MdsAHok/https-http-tomcat.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>3</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/04/https-http-tomcat.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-5235816251226303600</guid><pubDate>Mon, 30 Mar 2009 18:10:00 +0000</pubDate><atom:updated>2009-03-30T22:44:49.671+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">tapestry</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Замена механизма отображения ошибок при валидации в Tapestry 5.1</title><description>&lt;p&gt;При унифицированном подходе к отображению ошибок на форме, обычно для каждого поля ставится соответствующий html-элемент, либо элементы в которых пользователю сообщается об ошибке в конкретном поле. К примеру, в Spring-mvc в таком случае вам придется ставить тэг &lt;code&gt;form:error&lt;/code&gt; для каждого поля.&lt;/p&gt;&lt;p&gt;Механизм отображения ошибок в Tapestry 5 реализован с помощью специального декоратора, который призван избавить разработчика от расставления подобных элементов на своей странице. Реализация этого механизма находится в классе &lt;code&gt;org.apache.tapestry5.internal.DefaultValidationDecorator&lt;/code&gt;. Результат его работы выглядит так:&lt;/p&gt;&lt;div style="text-align:center;"&gt;&lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/c1HlAOhjir3Q7ceJe5aVNr/download/tapestry-wrong-validation.gif" border="0" alt=""/&gt;&lt;/div&gt;&lt;p&gt;Согласитесь, не очень красиво вставлять красный крест после каждого поля с ошибкой да еще и подсвечивать его содержимое. Во всяком случае меня это не устроило. Хотелось бы, чтобы ошибки выглядели примерно так:&lt;/p&gt;&lt;div style="text-align:center;"&gt;&lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/c9pS44hjir3Q_WeJe5aVNr/download/tapestry-validation.gif" border="0" alt=""/&gt;&lt;/div&gt;&lt;p&gt;Для этого, в модуле tapestry вашего приложения необходимо подменить markup-фильтр &lt;code&gt;DefaultValidationDecorator&lt;/code&gt; совоей реализацией:&lt;/p&gt;&lt;pre&gt;package com.blogspot.nkoksharov.tapestry;&lt;br /&gt;&lt;br /&gt;import org.apache.tapestry5.*;&lt;br /&gt;import org.apache.tapestry5.ioc.*;&lt;br /&gt;import org.apache.tapestry5.services.*;&lt;br /&gt;&lt;br /&gt;public class AppModule {&lt;br /&gt;&lt;br /&gt;    public void contributeMarkupRenderer(OrderedConfiguration&lt;MarkupRendererFilter&gt; configuration,&lt;br /&gt;                    final Environment environment) {&lt;br /&gt;&lt;br /&gt;        MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter() {&lt;br /&gt;            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) {&lt;br /&gt;                ValidationDecorator decorator = new ExValidationDecorator(environment, writer);&lt;br /&gt;                environment.push(ValidationDecorator.class, decorator);&lt;br /&gt;                renderer.renderMarkup(writer);&lt;br /&gt;                environment.pop(ValidationDecorator.class);&lt;br /&gt;            }&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;        &lt;b&gt;configuration.override("DefaultValidationDecorator", defaultValidationDecorator);&lt;/b&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Логика декоратора выглядит так:&lt;/p&gt;&lt;pre&gt;package com.blogspot.nkoksharov.tapestry;&lt;br /&gt;&lt;br /&gt;import org.apache.tapestry5.*;&lt;br /&gt;import org.apache.tapestry5.services.Environment;&lt;br /&gt;&lt;br /&gt;public class ExValidationDecorator extends BaseValidationDecorator {&lt;br /&gt;&lt;br /&gt;    private static final String ERROR_ELEMENT_CLASS = "error_element";&lt;br /&gt;&lt;br /&gt;    private final Environment environment;&lt;br /&gt;    private final MarkupWriter markupWriter;&lt;br /&gt;&lt;br /&gt;    public ExValidationDecorator(Environment environment, MarkupWriter markupWriter) {&lt;br /&gt;        this.environment = environment;&lt;br /&gt;        this.markupWriter = markupWriter;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void afterField(Field field) {&lt;br /&gt;        if (inError(field)) {&lt;br /&gt;            String errorId = field.getClientId() + "Error";&lt;br /&gt;            markupWriter.element("div",&lt;br /&gt;                    "class", ERROR_ELEMENT_CLASS,&lt;br /&gt;                    "id", errorId);&lt;br /&gt;            markupWriter.write(getErrorMsg(field));&lt;br /&gt;            markupWriter.end();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private String getErrorMsg(Field field) {&lt;br /&gt;        ValidationTracker tracker = environment.peekRequired(ValidationTracker.class);&lt;br /&gt;        return tracker.getError(field);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private boolean inError(Field field) {&lt;br /&gt;        ValidationTracker tracker = environment.peekRequired(ValidationTracker.class);&lt;br /&gt;        return tracker.inError(field);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Данный декоратор после каждого поля ставит такой вот элемент:&lt;/p&gt;&lt;pre&gt;&amp;lt;div class=&amp;quot;error_element&amp;quot; id=&amp;quot;someElementError&amp;quot;&amp;gt;&lt;br /&gt;    Сообщение об ошибке для данного поля&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;p&gt;Стоит отметить, что возможность переопределения элементов в списке конфигурации сервисов, через метод &lt;code&gt;override&lt;/code&gt; стала доступна лишь начиная с версии Tapestry 5.1.0.0&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-5235816251226303600?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/oFCax2GvKto" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/oFCax2GvKto/tapestry-51.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/03/tapestry-51.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-6963722378240117626</guid><pubDate>Sun, 22 Feb 2009 08:44:00 +0000</pubDate><atom:updated>2009-02-23T15:33:10.223+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">spring</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Динамическое добавление bean-ов в контекст Spring</title><description>&lt;p&gt;Хорошо известно, что Spring способен создавать bean-ы из статитческого контекста. Однако, имеется возможность динамического создания и регистрации bean-ов, которая в некоторых случаях может оказаться весьма полезной. Такую функциональность предоставляет интерфейс &lt;code&gt;org.springframework.beans.factory.support.BeanDefinitionRegistry&lt;/code&gt; метод &lt;code&gt;registerBeanDefinition&lt;/code&gt;. Для регистрации вам понадобится объект реализующий интерфейс &lt;code&gt;org.springframework.beans.factory.config.BeanDefinition&lt;/code&gt;. Созданием такого объекта занимается класс &lt;code&gt;org.springframework.beans.factory.support.BeanDefinitionBuilder&lt;/code&gt;, предоставляющий практически все те же возможности конфигурации bean-а, что и в статическом xml. При регистрации bean-а, если bean c таким id уже существует, то он будет заменен.&lt;/p&gt;&lt;pre&gt;package com.blogspot.nkoksharov;&lt;br /&gt;&lt;br /&gt;import org.springframework.beans.factory.config.BeanDefinition;&lt;br /&gt;import org.springframework.beans.factory.support.BeanDefinitionBuilder;&lt;br /&gt;import org.springframework.beans.factory.support.BeanDefinitionRegistry;&lt;br /&gt;import org.springframework.context.ConfigurableApplicationContext;&lt;br /&gt;import org.springframework.context.support.ClassPathXmlApplicationContext;&lt;br /&gt;import org.testng.annotations.Test;&lt;br /&gt;&lt;br /&gt;public class SpringTest {&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testDynamicCreation() {&lt;br /&gt;        ConfigurableApplicationContext context = &lt;br /&gt;             new ClassPathXmlApplicationContext("/spring/test/application-context.xml");&lt;br /&gt;        BeanDefinitionRegistry factory = (BeanDefinitionRegistry) context.getBeanFactory();&lt;br /&gt;        &lt;br /&gt;        &lt;b&gt;BeanDefinitionBuilder builder = &lt;br /&gt;             BeanDefinitionBuilder.rootBeanDefinition(com.blogspot.nkoksharov.TestBeanImpl.class);&lt;br /&gt;        builder.addPropertyValue("property", "someValue");&lt;br /&gt;        BeanDefinition definition = builder.getBeanDefinition();&lt;br /&gt;        factory.registerBeanDefinition("testBean", definition);&lt;/b&gt;&lt;br /&gt;        &lt;br /&gt;        ... &lt;br /&gt;&lt;br /&gt;        TestBean bean = (TestBean) context.getBean("testBean");&lt;br /&gt;&lt;br /&gt;        ...&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Также можно и удалять bean-ы методом &lt;code&gt;removeBeanDefinition&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-6963722378240117626?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/Pb1_h1dBvjI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/Pb1_h1dBvjI/bean-spring.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/02/bean-spring.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-2717647403924973492</guid><pubDate>Thu, 19 Feb 2009 15:46:00 +0000</pubDate><atom:updated>2009-02-19T21:12:26.506+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">maven-plugin</category><category domain="http://www.blogger.com/atom/ns#">spring</category><category domain="http://www.blogger.com/atom/ns#">thought</category><title>Сборка приложения используя maven-assembly-plugin с модулями Spring</title><description>&lt;p&gt;Столкнулся с проблемой сборки приложения при помощи &lt;code&gt;maven-assembly-plugin&lt;/code&gt;. Напомню, что этот плагин предназначен для создания архива, включающего в себя в распакованном виде какие-либо ресурсы, а также классы из зависимостей проекта. То есть, если вам нужно поместить приложение со всеми его зависимыми классами из других библиотек в один jar-файл, то этот инструмент может вам помочь.&lt;/p&gt;&lt;p&gt;Однако по причине того, что плагин распаковывает все зависимости в один каталог, могу возникнуть проблемы с библиотеками, имеющими файлы с одинаковыми названиями. Именно такая ситуация и происходит, когда пытаешься собрать приложение в один jar имеющее в зависимостях spring библиотеки. После запуска приложения возникает такая ошибка:&lt;/p&gt;&lt;pre&gt;Exception in thread "main" java.lang.IllegalStateException: &lt;br /&gt;org.springframework.beans.factory.parsing.BeanDefinitionParsingException: &lt;br /&gt;Configuration problem: Failed to import bean definitions &lt;br /&gt;   from URL location [classpath:/spring/dao-context.xml]&lt;br /&gt;Caused by: &lt;br /&gt;org.springframework.beans.factory.parsing.BeanDefinitionParsingException: &lt;br /&gt;Configuration problem: Unable to locate Spring NamespaceHandler for &lt;br /&gt;XML schema namespace [http://www.springframework.org/schema/tx]&lt;br /&gt;Offending resource: class path resource [spring/dao-context.xml]&lt;br /&gt;&lt;br /&gt;   at org.springframework.beans.factory.parsing.&lt;br /&gt;FailFastProblemReporter.error(FailFastProblemReporter.java:68)&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Возникает она из-за того, что spring не может найти соответствующий &lt;code&gt;NamespaceHandler&lt;/code&gt; предназначенный для обработки xml-схемы &lt;code&gt;http://www.springframework.org/schema/tx&lt;/code&gt;. Соответствие между NamespaceHandler-ми и xml-схемами описывается в файле &lt;code&gt;spring.handlers&lt;/code&gt;. Файл &lt;code&gt;spring.handlers&lt;/code&gt; имеется в нескольких библиотеках spring (spring-aop.jar, spring-beans.jar...) в папке META-INF, таким образом в наш jar попадает лишь какой-то определенный экземпляр.При дублировании файлов &lt;code&gt;maven-assembly-plugin&lt;/code&gt; в результате оставляет первый из них, merge-ить их он не умеет. Та же ситуация происходит и с файлом &lt;code&gt;spring.schemas&lt;/code&gt;, в котором описываются соотвествия путей к xsd описаниям xml-схем и их реальным положением внутри библиотеки spring. &lt;/p&gt;&lt;p&gt;Решить возникшую проблему можно создав файлы &lt;a href="https://www.assembla.com/spaces/nkoksharov/documents/bsa7LW_O4r3z6QeJe5afGb/download/spring.schemas"&gt;spring.schemas&lt;/a&gt; и &lt;a href="https://www.assembla.com/spaces/nkoksharov/documents/bkip4G_O4r3yjAeJe5aVNr/download/spring.handlers"&gt;spring.handlers&lt;/a&gt; в папке &lt;code&gt;/src/main/resources/META-INF&lt;/code&gt; вашего maven-проекта, агрегирующих в себе содержимое одноименных файлов из библиотек. В файле настройки &lt;code&gt;maven-assembly-plugin&lt;/code&gt;-а необходимо указать папку &lt;code&gt;/src/main/resources/META-INF&lt;/code&gt; которую мы будем копировать в архив, с помощью тэгов &lt;code&gt;fileSets&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&amp;lt;assembly&amp;gt;&lt;br /&gt;    &amp;lt;id&amp;gt;with-dependencies&amp;lt;/id&amp;gt;&lt;br /&gt;    &amp;lt;formats&amp;gt;&lt;br /&gt;        &amp;lt;format&amp;gt;jar&amp;lt;/format&amp;gt;&lt;br /&gt;    &amp;lt;/formats&amp;gt;&lt;br /&gt;    &amp;lt;includeBaseDirectory&amp;gt;false&amp;lt;/includeBaseDirectory&amp;gt;&lt;br /&gt;    &lt;b&gt;&amp;lt;fileSets&amp;gt;&lt;br /&gt;      &amp;lt;fileSet&amp;gt;&lt;br /&gt;        &amp;lt;directory&amp;gt;${basedir}/src/main/resources/META-INF&amp;lt;/directory&amp;gt;&lt;br /&gt;        &amp;lt;outputDirectory&amp;gt;META-INF&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;      &amp;lt;/fileSet&amp;gt;&lt;br /&gt;    &amp;lt;/fileSets&amp;gt;&lt;/b&gt;&lt;br /&gt;    &amp;lt;dependencySets&amp;gt;&lt;br /&gt;        &amp;lt;dependencySet&amp;gt;&lt;br /&gt;            &amp;lt;outputDirectory/&amp;gt;&lt;br /&gt;            &amp;lt;outputFileNameMapping/&amp;gt;&lt;br /&gt;            &amp;lt;unpack&amp;gt;true&amp;lt;/unpack&amp;gt;&lt;br /&gt;        &amp;lt;/dependencySet&amp;gt;&lt;br /&gt;    &amp;lt;/dependencySets&amp;gt;&lt;br /&gt;&amp;lt;/assembly&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Таким образом наши файлы попадут в архив первыми и уже не будут перезаписаны другими вариантами из библиотек Spring.&lt;/p&gt;&lt;p&gt;Кстати, версию &lt;code&gt;maven-assembly-plugin&lt;/code&gt;-а следует использовать &lt;code&gt;2.2-beta-3&lt;/code&gt; и старше. Предыдующая версия будет копировать все одинаковые файлы в ваш jar, и в одной папке будет несколько файлов с одним именем.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-2717647403924973492?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/3UhdWDllmWo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/3UhdWDllmWo/maven-assembly-plugin-spring.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>7</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/02/maven-assembly-plugin-spring.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-3541589794468264457</guid><pubDate>Sat, 07 Feb 2009 18:46:00 +0000</pubDate><atom:updated>2009-02-09T14:09:19.347+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">tool</category><category domain="http://www.blogger.com/atom/ns#">logging</category><title>Инструмент для просмотра логов</title><description>&lt;p&gt;В повседневной работе довольно часто приходится смотреть логи от log4j. Прежде пользовался лишь обычным view-ром, пока не нашел довольно полезный open-source инструмент &lt;a href="http://mindtreeinsight.sourceforge.net/"&gt;MindTree Insight&lt;/a&gt;, предназначенный именно для этой задачи.&lt;/p&gt;&lt;p&gt;Из настроек отмечу лишь, то что поле Preferences -&gt; Maintain Preferences -&gt; Primary Pattern должно содержать ту же строку форматирования, которую вы указали в настройках log4j.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-3541589794468264457?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/-e8wYf1Lflw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/-e8wYf1Lflw/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>8</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/02/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-193305696230363908</guid><pubDate>Mon, 02 Feb 2009 15:10:00 +0000</pubDate><atom:updated>2009-02-02T18:11:15.568+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">bug</category><category domain="http://www.blogger.com/atom/ns#">spring security</category><title>Проблема с SecurityContext-ом при invalidate http-сессии</title><description>&lt;p&gt;В Spring Security есть возможность определения допустимого числа параллельных http-сессий от одного пользователя. Как только кол-во свободных сессий исчерпает себя, то по-умолчанию самая первая из них будет уничтожена через метод invalidate() и ее место займет новая. За настройку такой возможности отвечает тэг &lt;code&gt;concurrent-session-control&lt;/code&gt; и его параметр &lt;code&gt;max-sessions&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Проблема, которая была обнаружена мной буквально на днях, следующая - при уничтожении сессии, вместо объекта &lt;code&gt;org.springframework.security.context.SecurityContext&lt;/code&gt; приходит &lt;code&gt;null&lt;/code&gt;. Выявить эту ошибку может следующий код:&lt;/p&gt;&lt;pre&gt;public void onApplicationEvent(ApplicationEvent event) {&lt;br /&gt;    if (!(event instanceof HttpSessionDestroyedEvent)) {&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    SecurityContext context = SecurityContextHolder.getContext();&lt;br /&gt;    Object principal = context.getAuthentication().getPrincipal();&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Если посмотреть порядок следования фильтров в Spring Security:&lt;ol&gt;&lt;li&gt;&lt;p&gt;CHANNEL_FILTER&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;CONCURRENT_SESSION_FILTER&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;SESSION_CONTEXT_INTEGRATION_FILTER&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;LOGOUT_FILTER&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;...&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;то мы увидим, что фильтр &lt;code&gt;SESSION_CONTEXT_INTEGRATION_FILTER&lt;/code&gt;, который как раз и выставляет нужный нам объект SecurityContext, находится после &lt;code&gt;CONCURRENT_SESSION_FILTER&lt;/code&gt; фильтра, управляющего параллельными сессиями. Такая "неверная", в данном случае, конфигурация фильтров и приводит к потере SecurityContext-а. Потому завел на это дело багу - &lt;a href="http://jira.springframework.org/browse/SEC-1092"&gt;SEC-1092&lt;/a&gt;. Решения я увидел два - поменять фильтры местами, с помощью конфигурации это делается довольно просто, и выставить объект SecurityContext самому. Второй вариант мне показался более верным. Менять фильтры местами, думаю не стоит, возможно есть причина по которой они расположены именно так.&lt;/p&gt;&lt;p&gt;Нужный нам экземпляр SecurityContext-а можно взять из текущий http-сессии, к которой он и привязан. Хранится он в сессии по имени &lt;code&gt;HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY&lt;/code&gt;. Объект класса &lt;code&gt;HttpSessionContextIntegrationFilter&lt;/code&gt; как раз и находится на позиции &lt;code&gt;SESSION_CONTEXT_INTEGRATION_FILTER&lt;/code&gt;, и выставляет текущий &lt;code&gt;SecurityContext&lt;/code&gt; в &lt;code&gt;SecurityContextHolder&lt;/code&gt;. Сам код решения взят из этого фильтра и выглядит так:&lt;/p&gt;&lt;pre&gt;public void onApplicationEvent(ApplicationEvent event) {&lt;br /&gt;    if (!(event instanceof HttpSessionDestroyedEvent)) {&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;b&gt;HttpSession session = event.getSession();&lt;br /&gt;    SecurityContext context = (SecurityContext) session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY);&lt;br /&gt;    SecurityContextHolder.setContext(context);&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;    SecurityContext context = SecurityContextHolder.getContext();&lt;br /&gt;    Object principal = context.getAuthentication().getPrincipal();&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-193305696230363908?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/I33qJPbtzNM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/I33qJPbtzNM/securitycontext-invalidate-http.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/02/securitycontext-invalidate-http.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-6465880190774341896</guid><pubDate>Tue, 20 Jan 2009 09:05:00 +0000</pubDate><atom:updated>2009-01-20T17:05:00.462+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">maven-plugin</category><category domain="http://www.blogger.com/atom/ns#">flex</category><title>Генерация flex-проекта с помощью Maven</title><description>&lt;p&gt;До недавнего времени, создавать flex-проекты мне приходилось вручную. Откровенно говоря этот процесс меня не очень радовал, в результате поисков был найден maven-плагин &lt;a href="http://maven.servebox.org/sites/maven-flex-plugin/flex-plugin/"&gt;Maven Flex Plugin&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Этот плагин помимо типичных задач компиляции swc и swf-файлов, которые способны выполнять и ряд других плагинов (например &lt;a href="http://code.google.com/p/flex-mojos/"&gt;Flex-mojos&lt;/a&gt;), также позволяет выполнять очень ценную задачу &lt;code&gt;flex:eclipse&lt;/code&gt; - подобие &lt;code&gt;eclipse:eclipse&lt;/code&gt;, привычного, думаю для всех, способа геренации java-проектов под Eclipse. Для его настройки, в pom-файле необходимо зарегистрировать плагин-репозитарий:&lt;/p&gt;&lt;pre&gt;    &amp;lt;pluginRepositories&amp;gt;&lt;br /&gt;        &amp;lt;pluginRepository&amp;gt;&lt;br /&gt;          &amp;lt;id&amp;gt;flex-plugin-repo&amp;lt;/id&amp;gt;&lt;br /&gt;          &amp;lt;name&amp;gt;Flex Plugin&amp;lt;/name&amp;gt;&lt;br /&gt;          &amp;lt;url&amp;gt;http://maven.servebox.org/repository/&amp;lt;/url&amp;gt;&lt;br /&gt;          &amp;lt;layout&amp;gt;default&amp;lt;/layout&amp;gt;&lt;br /&gt;          &amp;lt;snapshots&amp;gt;&lt;br /&gt;            &amp;lt;enabled&amp;gt;false&amp;lt;/enabled&amp;gt;&lt;br /&gt;          &amp;lt;/snapshots&amp;gt;&lt;br /&gt;          &amp;lt;releases&amp;gt;&lt;br /&gt;            &amp;lt;updatePolicy&amp;gt;never&amp;lt;/updatePolicy&amp;gt;&lt;br /&gt;          &amp;lt;/releases&amp;gt;&lt;br /&gt;        &amp;lt;/pluginRepository&amp;gt;&lt;br /&gt;    &amp;lt;/pluginRepositories&amp;gt;&lt;/pre&gt;&lt;p&gt;указать сам плагин:&lt;/p&gt;&lt;pre&gt;    &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;org.servebox.flex&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;flex-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;extensions&amp;gt;true&amp;lt;/extensions&amp;gt;&lt;br /&gt;        &amp;lt;inherited&amp;gt;true&amp;lt;/inherited&amp;gt;&lt;br /&gt;    &amp;lt;/plugin&amp;gt;&lt;/pre&gt;&lt;p&gt;Теперь можно выполнять: &lt;code&gt;mvn flex:eclipse&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Не забудьте определить переменную &lt;code&gt;M2_REPO&lt;/code&gt; указывающую на ваш репозитарий во Flex Builder-е, на вкладке &lt;code&gt;Window &gt; Preferences &gt; General &gt; Workspace &gt; Linked Resources&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Плагин также регистрирует в создаваемых пректах зависимости на другие swc-библиотеки. Таким образом его достаточно запустить на уровне головного pom-файла, чтобы сгенерировать все дочерние проекты. Хочу отметить, что планиг отлично уживается в одном проекте с Flex-mojos.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-6465880190774341896?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/crSFKMVI3gk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/crSFKMVI3gk/flex-maven.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>2</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/01/flex-maven.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-416251885465754146</guid><pubDate>Wed, 31 Dec 2008 21:48:00 +0000</pubDate><atom:updated>2008-12-31T20:16:32.948+03:00</atom:updated><title>С Новым Годом!!!</title><description>&lt;p&gt;Дорогие читатели, поздравляю Вас с Новым Годом!!! Хочу пожелать чтобы все мечты, загаданные под бой курантов, обязательно исполнились. Пусть рядом с Вами всегда будут хорошие люди, пусть все трудности и неприятности пройдут мимо Вас. И конечно же желаю Вам просто огромных успехов в работе.&lt;/p&gt;&lt;p&gt;С Новым... .-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-416251885465754146?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/I7KumGxNB6s" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/I7KumGxNB6s/blog-post_31.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>2</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/12/blog-post_31.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-3083973996914558656</guid><pubDate>Wed, 31 Dec 2008 12:17:00 +0000</pubDate><atom:updated>2008-12-31T19:41:56.374+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">tool</category><category domain="http://www.blogger.com/atom/ns#">database</category><title>Миграция данных между БД</title><description>&lt;p&gt;Перемещение/преобразование данных между разными БД требует использование хорошо отлаженного механизма выполнения sql-запросов, которые необходимы для импорта/экспорта данных между БД. Написание такого рода движка потребует не мало времени и усилий.&lt;/p&gt;&lt;p&gt;Среди open-source решений на java мне известны два инструмента решающих подобную задачу - &lt;a href="http://jdbcimporter.sourceforge.net/"&gt;JDBCImporter&lt;/a&gt; и &lt;a href="http://scriptella.javaforge.com/"&gt;Scriptella&lt;/a&gt;. Первый инструмент делает экспорт данных сначала в xml, а затем производит из него импорт, т.е. имеет промежуточный xml при обработке данных. Сам JDBCImporter имеет утечки памяти при обработки данных больших объемов, да и как-то не активно развивается.&lt;/p&gt;&lt;p&gt;Scriptella - это инструмент для выполнения ETL (Extract-Transform-Load) процесса, который позволяет собирать данные из одного или нескольких источников данных и загружать их в другие позволяя выполнять различного рода трасформацию самих данных. При трансформации данных можно использовать скрипты прямо в sql-запросах, написанных на Velocity, JavaScript, JEXL... Сама конфигурация etl-процесса описывается в xml файле:&lt;/p&gt;&lt;pre&gt;&amp;lt;!DOCTYPE etl SYSTEM &amp;quot;http://scriptella.javaforge.com/dtd/etl.dtd&amp;quot;&amp;gt;&lt;br /&gt;&amp;lt;etl&amp;gt;&lt;br /&gt;    &amp;lt;description&amp;gt;&amp;lt;/description&amp;gt;&lt;br /&gt;    &amp;lt;properties&amp;gt;&lt;br /&gt;        &amp;lt;include href=&amp;quot;script.properties&amp;quot;/&amp;gt;&lt;br /&gt;        driver=org.jdcDriver&lt;br /&gt;    &amp;lt;/properties&amp;gt;&lt;br /&gt;    &amp;lt;connection id=&amp;quot;con1&amp;quot; driver=&amp;quot;$driver&amp;quot; url=&amp;quot;${url}&amp;quot; user=&amp;quot;$user&amp;quot; password=&amp;quot;12345678&amp;quot;&amp;gt;&lt;br /&gt;        driver.property=value&lt;br /&gt;    &amp;lt;/connection&amp;gt;&lt;br /&gt;    &amp;lt;connection id=&amp;quot;con2&amp;quot; url=&amp;quot;jdbc:hsqldb:file:db&amp;quot; user=&amp;quot;sa&amp;quot; password=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;script connection-id=&amp;quot;out&amp;quot;&amp;gt;&lt;br /&gt;     &amp;lt;include href=&amp;quot;dbschema.sql&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;query connection-id=&amp;quot;in&amp;quot;&amp;gt;&lt;br /&gt;        SELECT * from Bug&lt;br /&gt;        &amp;lt;script connection-id=&amp;quot;out&amp;quot;&amp;gt;&lt;br /&gt;            INSERT INTO Bug VALUES (?ID, ?priority, ?summary, ?status);&lt;br /&gt;        &amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;/query&amp;gt;&lt;br /&gt;&amp;lt;/etl&amp;gt;&lt;/pre&gt;&lt;p&gt;В описании конфигурации выделяют три основных элемента:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;connection&lt;/b&gt; - представляет собой соединение с источником данных, таковым может являться как БД, XLS, CSV, XML... Можно также реализовать свой драйвер источника данных для Scriptella.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;script&lt;/b&gt; - скрипт, написанный на языке источника данных к которому он относится через connection-id. Именно здесь выполняются различные скрипты на Velocity, JavaScript, JEXL и т.д.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;query&lt;/b&gt; - запрос, выполняемый на источнике данных connection-id. Может содержать любое кол-во вложенных &lt;b&gt;query&lt;/b&gt; и &lt;b&gt;script&lt;/b&gt; элементов.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Запуск Scriptella можно произвести из командной строки, с помощью Ant-а, либо прямиком из Java.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-3083973996914558656?l=nkoksharov.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/JBvFim04Ev0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/JBvFim04Ev0/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/12/blog-post.html</feedburner:origLink></item></channel></rss>

