<?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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-757496094257544869</atom:id><lastBuildDate>Thu, 05 Nov 2009 16:00:02 +0000</lastBuildDate><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>33</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/nkoksharov" type="application/rss+xml" /><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-6634198279393130409</guid><pubDate>Wed, 28 Oct 2009 12:24:00 +0000</pubDate><atom:updated>2009-10-28T15:33:27.583+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;pre&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;}&lt;br /&gt;&lt;/pre&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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">0</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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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>2009-08-18T18:43:20.768+04: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;pre&gt;&amp;lt;properties&amp;gt;&lt;br /&gt;      &amp;lt;hibernate.update&amp;gt;false&amp;lt;/hibernate.update&amp;gt;&lt;br /&gt;      &amp;lt;hibernate.create&amp;gt;false&amp;lt;/hibernate.create&amp;gt;&lt;br /&gt;&amp;lt;/properties&amp;gt;&lt;br /&gt;&amp;lt;build&amp;gt;&lt;br /&gt;      &amp;lt;plugins&amp;gt;&lt;br /&gt;          ...&lt;br /&gt;          &amp;lt;plugin&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;hibernate3-maven-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;2.2&amp;lt;/version&amp;gt;&lt;br /&gt;            &amp;lt;configuration&amp;gt;&lt;br /&gt;              &amp;lt;componentProperties&amp;gt;&lt;br /&gt;                &amp;lt;outputfilename&amp;gt;schema.sql&amp;lt;/outputfilename&amp;gt;&lt;br /&gt;                &amp;lt;configurationfile&amp;gt;/resources/hibernate.cfg.xml&amp;lt;/configurationfile&amp;gt;&lt;br /&gt;                &amp;lt;export&amp;gt;false&amp;lt;/export&amp;gt;&lt;br /&gt;                &amp;lt;update&amp;gt;${hibernate.update}&amp;lt;/update&amp;gt;&lt;br /&gt;                &amp;lt;create&amp;gt;${hibernate.create}&amp;lt;/create&amp;gt;&lt;br /&gt;                &amp;lt;drop&amp;gt;true&amp;lt;/drop&amp;gt;&lt;br /&gt;              &amp;lt;/componentProperties&amp;gt;&lt;br /&gt;            &amp;lt;/configuration&amp;gt;&lt;br /&gt;            &amp;lt;dependencies&amp;gt;&lt;br /&gt;                &amp;lt;dependency&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;postgresql&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;postgresql&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                    &amp;lt;version&amp;gt;8.3-603.jdbc4&amp;lt;/version&amp;gt;&lt;br /&gt;                &amp;lt;/dependency&amp;gt;&lt;br /&gt;            &amp;lt;/dependencies&amp;gt;&lt;br /&gt;          &amp;lt;/plugin&amp;gt;&lt;br /&gt;      &amp;lt;/plugins&amp;gt;&lt;br /&gt;&amp;lt;/build&amp;gt;&lt;/pre&gt;&lt;p&gt;Необоходимым условием для работы плагина является наличие файла &lt;code&gt;hibernate.hbm.xml&lt;/code&gt;, в нем должны содержаться следующие настройки:&lt;/p&gt;&lt;pre&gt;&amp;lt;hibernate-configuration&amp;gt;&lt;br /&gt;    &amp;lt;session-factory&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;hibernate.connection.driver_class&amp;quot;&amp;gt;...&amp;lt;/property&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;hibernate.connection.url&amp;quot;&amp;gt;...&amp;lt;/property&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;hibernate.connection.username&amp;quot;&amp;gt;...&amp;lt;/property&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;hibernate.connection.password&amp;quot;&amp;gt;...&amp;lt;/property&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;hibernate.dialect&amp;quot;&amp;gt;...&amp;lt;/property&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;mapping class=&amp;quot;ru.somesite.SomeObject&amp;quot;/&amp;gt;&lt;br /&gt;        ...&lt;br /&gt;    &amp;lt;/session-factory&amp;gt;&lt;br /&gt;&amp;lt;/hibernate-configuration&amp;gt;&lt;/pre&gt;&lt;p&gt;Не забудьте также и о том, что теперь все маппинги классов тоже нужно указывать в этом файле, а не в каком-нибудь SessionFactoryBean, если используете Spring. Настройка SessionFactoryBean будет выглядеть примерно так:&lt;/p&gt;&lt;pre&gt;&amp;lt;bean id=&amp;quot;sessionFactory&amp;quot;&lt;br /&gt;        class=&amp;quot;org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;dataSource&amp;quot; ref=&amp;quot;dataSource&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;configLocation&amp;quot; value=&amp;quot;classpath:/hibernate.cfg.xml&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;hibernateProperties&amp;quot;&amp;gt;&lt;br /&gt;            &amp;lt;props&amp;gt;&lt;br /&gt;                ...&lt;br /&gt;                &amp;lt;prop key=&amp;quot;hibernate.hbm2ddl.auto&amp;quot;&amp;gt;&lt;b&gt;validate&lt;/b&gt;&amp;lt;/prop&amp;gt;&lt;br /&gt;            &amp;lt;/props&amp;gt;&lt;br /&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;/pre&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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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>2009-07-14T14:00:20.473+04: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;pre&gt;&amp;lt;databaseChangeLog&lt;br /&gt;        xmlns=&amp;quot;http://www.liquibase.org/xml/ns/dbchangelog/1.9&amp;quot;&lt;br /&gt;        xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;        xsi:schemaLocation=&amp;quot;http://www.liquibase.org/xml/ns/dbchangelog/1.9 &lt;br /&gt;                http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd&amp;quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;include file=&amp;quot;changelogs/1.0/release-changelog.xml&amp;quot; relativeToChangelogFile=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;     &amp;lt;include file=&amp;quot;changelogs/1.1/release-changelog.xml&amp;quot; relativeToChangelogFile=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;     &amp;lt;include file=&amp;quot;changelogs/2.0/release-changelog.xml&amp;quot; relativeToChangelogFile=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;     ...&lt;br /&gt;&amp;lt;/databaseChangeLog&amp;gt;&lt;/pre&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;pre&gt;&amp;lt;changeSet id=&amp;quot;2&amp;quot; author=&amp;quot;nkoksharov&amp;quot; context=&amp;quot;initdata&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;sql&amp;gt;&lt;br /&gt;           insert into roles values (123, &amp;#039;ROLE_USER&amp;#039;);&lt;br /&gt;        &amp;lt;/sql&amp;gt;&lt;br /&gt;        &amp;lt;rollback&amp;gt;&lt;br /&gt;            &amp;lt;sql&amp;gt;&lt;br /&gt;               delete from roles where id = 123;&lt;br /&gt;            &amp;lt;/sql&amp;gt;&lt;br /&gt;        &amp;lt;/rollback&amp;gt;&lt;br /&gt;&amp;lt;/changeSet&amp;gt;&lt;/pre&gt;&lt;p&gt;Если в проекте предполагается использовать две базы - БД разработчика/продакшн и БД для тестов, то pom-файл, созданного нами артефакта, будет содержать следующие строки:&lt;/p&gt;&lt;pre&gt;&amp;lt;project&amp;gt;&lt;br /&gt;  &amp;lt;properties&amp;gt;&lt;br /&gt;      &amp;lt;liquibase.goal&amp;gt;update&amp;lt;/liquibase.goal&amp;gt;&lt;br /&gt;  &amp;lt;/properties&amp;gt;&lt;br /&gt;  &amp;lt;profiles&amp;gt;&lt;br /&gt; &amp;lt;profile&amp;gt;&lt;br /&gt;  &amp;lt;id&amp;gt;devdb&amp;lt;/id&amp;gt;&lt;br /&gt;  &amp;lt;activation&amp;gt;&lt;br /&gt;   &amp;lt;activeByDefault&amp;gt;true&amp;lt;/activeByDefault&amp;gt;&lt;br /&gt;  &amp;lt;/activation&amp;gt;&lt;br /&gt;  &amp;lt;properties&amp;gt;&lt;br /&gt;   &amp;lt;database.url&amp;gt;${app.database.url}&amp;lt;/database.url&amp;gt;&lt;br /&gt;   &amp;lt;database.user&amp;gt;${app.database.user}&amp;lt;/database.user&amp;gt;&lt;br /&gt;   &amp;lt;database.password&amp;gt;${app.database.password}&amp;lt;/database.password&amp;gt;&lt;br /&gt;   &amp;lt;database.contexts&amp;gt;${app.database.contexts}&amp;lt;/database.contexts&amp;gt;&lt;br /&gt;  &amp;lt;/properties&amp;gt;&lt;br /&gt; &amp;lt;/profile&amp;gt;&lt;br /&gt;    &amp;lt;profile&amp;gt;&lt;br /&gt;        &amp;lt;id&amp;gt;testdb&amp;lt;/id&amp;gt;&lt;br /&gt;        &amp;lt;properties&amp;gt;&lt;br /&gt;   &amp;lt;database.url&amp;gt;${test.database.url}&amp;lt;/database.url&amp;gt;&lt;br /&gt;   &amp;lt;database.user&amp;gt;${test.database.user}&amp;lt;/database.user&amp;gt;&lt;br /&gt;   &amp;lt;database.password&amp;gt;${test.database.password}&amp;lt;/database.password&amp;gt;&lt;br /&gt;   &amp;lt;database.contexts&amp;gt;${test.database.contexts}&amp;lt;/database.contexts&amp;gt;&lt;br /&gt;       &amp;lt;/properties&amp;gt;&lt;br /&gt;    &amp;lt;/profile&amp;gt;&lt;br /&gt;  &amp;lt;/profiles&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;build&amp;gt;&lt;br /&gt;    &amp;lt;plugins&amp;gt;&lt;br /&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;org.liquibase&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;liquibase-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;version&amp;gt;1.9.3.0&amp;lt;/version&amp;gt;&lt;br /&gt;          &amp;lt;configuration&amp;gt;&lt;br /&gt;            &amp;lt;changeLogFile&amp;gt;src/main/resources/project-changelogs.xml&amp;lt;/changeLogFile&amp;gt;&lt;br /&gt;            &amp;lt;driver&amp;gt;org.postgresql.Driver&amp;lt;/driver&amp;gt;&lt;br /&gt;&lt;br /&gt;            &amp;lt;url&amp;gt;${database.url}&amp;lt;/url&amp;gt;&lt;br /&gt;            &amp;lt;username&amp;gt;${database.user}&amp;lt;/username&amp;gt;&lt;br /&gt;            &amp;lt;password&amp;gt;${database.password}&amp;lt;/password&amp;gt;&lt;br /&gt;            &amp;lt;contexts&amp;gt;${database.contexts}&amp;lt;/contexts&amp;gt;&lt;br /&gt;          &amp;lt;/configuration&amp;gt;&lt;br /&gt;          &amp;lt;executions&amp;gt;&lt;br /&gt;            &amp;lt;execution&amp;gt;&lt;br /&gt;              &amp;lt;id&amp;gt;appdb&amp;lt;/id&amp;gt;&lt;br /&gt;              &amp;lt;phase&amp;gt;process-resources&amp;lt;/phase&amp;gt;&lt;br /&gt;              &amp;lt;configuration&amp;gt;&lt;br /&gt;                 &amp;lt;contexts&amp;gt;${app.database.contexts}&amp;lt;/contexts&amp;gt;&lt;br /&gt;                 &amp;lt;url&amp;gt;${app.database.url}&amp;lt;/url&amp;gt;&lt;br /&gt;                 &amp;lt;username&amp;gt;${app.database.user}&amp;lt;/username&amp;gt;&lt;br /&gt;                 &amp;lt;password&amp;gt;${app.database.password}&amp;lt;/password&amp;gt;&lt;br /&gt;              &amp;lt;/configuration&amp;gt;&lt;br /&gt;              &amp;lt;goals&amp;gt;&lt;br /&gt;                 &amp;lt;goal&amp;gt;${liquibase.goal}&amp;lt;/goal&amp;gt;&lt;br /&gt;              &amp;lt;/goals&amp;gt;&lt;br /&gt;            &amp;lt;/execution&amp;gt;&lt;br /&gt;            &amp;lt;execution&amp;gt;&lt;br /&gt;              &amp;lt;id&amp;gt;testdb&amp;lt;/id&amp;gt;&lt;br /&gt;              &amp;lt;phase&amp;gt;process-resources&amp;lt;/phase&amp;gt;&lt;br /&gt;              &amp;lt;configuration&amp;gt;&lt;br /&gt;                 &amp;lt;contexts&amp;gt;${test.database.contexts}&amp;lt;/contexts&amp;gt;&lt;br /&gt;                 &amp;lt;url&amp;gt;${test.database.url}&amp;lt;/url&amp;gt;&lt;br /&gt;                 &amp;lt;username&amp;gt;${test.database.user}&amp;lt;/username&amp;gt;&lt;br /&gt;                 &amp;lt;password&amp;gt;${test.database.password}&amp;lt;/password&amp;gt;&lt;br /&gt;              &amp;lt;/configuration&amp;gt;&lt;br /&gt;              &amp;lt;goals&amp;gt;&lt;br /&gt;                 &amp;lt;goal&amp;gt;${liquibase.goal}&amp;lt;/goal&amp;gt;&lt;br /&gt;              &amp;lt;/goals&amp;gt;&lt;br /&gt;            &amp;lt;/execution&amp;gt;&lt;br /&gt;          &amp;lt;/executions&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;    &amp;lt;/plugins&amp;gt;&lt;br /&gt;  &amp;lt;/build&amp;gt;&lt;br /&gt;  ...&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;/pre&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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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-2946891598163709962</guid><pubDate>Thu, 09 Jul 2009 07:33:00 +0000</pubDate><atom:updated>2009-07-09T22:00:03.593+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">maven-plugin</category><category domain="http://www.blogger.com/atom/ns#">maven</category><title>Ускорение выполнения команд maven</title><description>&lt;p&gt;На днях мне рассказали про maven-cli-plugin и я решил поспешить рассказать вам об этом окрытии. Думаю, у каждого, кто работал с maven, возникало желание хоть как-то ускорить выполнение его команд. Теперь такая возможность появилась в виде реализации выше названного плагина.&lt;/p&gt;&lt;p&gt;Плангин, после запуска, предоставляет разработчику командную строку, в которой вы можете запускать различные фазы жизненного цикла - clean, install, test ... (в режиме &lt;code&gt;execute-phase&lt;/code&gt;), либо другие плагины (в режиме &lt;code&gt;execute&lt;/code&gt;). Из-за того, что плагин, поднимает в себе инфраструктуру maven-а, то время выполнения команд, на некоторых модулях, может уменьшится в 2 раза! Первое выполнение команды может ни чем не отличаться по времени, но последующие будут быстрыми.&lt;/p&gt;&lt;p&gt;Я пока использую лишь &lt;code&gt;execute-phase&lt;/code&gt;. Причем очень удобной оказалась возможность собирать несколько модулей с одной фазой, перечисляя их через пробел:&lt;/p&gt;&lt;pre&gt;            module1 module2 clean install&lt;/pre&gt;&lt;p&gt;Чтобы подключитьданный плагин, в pom-файле нужно указать сам плагин:&lt;/p&gt;&lt;pre&gt;  &amp;lt;plugins&amp;gt;&lt;br /&gt;        &amp;lt;plugin&amp;gt;&lt;br /&gt;             &amp;lt;groupId&amp;gt;org.twdata.maven&amp;lt;/groupId&amp;gt;&lt;br /&gt;             &amp;lt;artifactId&amp;gt;maven-cli-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;/plugin&amp;gt;&lt;br /&gt;  &amp;lt;/plugins&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;И репозитарий для него:&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;jboss&amp;lt;/id&amp;gt;&lt;br /&gt;         &amp;lt;name&amp;gt;JBoss Repository&amp;lt;/name&amp;gt;&lt;br /&gt;         &amp;lt;url&amp;gt;http://repository.jboss.org/maven2&amp;lt;/url&amp;gt;&lt;br /&gt;      &amp;lt;/pluginRepository&amp;gt;&lt;br /&gt;  &amp;lt;/pluginRepositories&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Запуск выполняется командой, на уровне корневого pom-файла:&lt;/p&gt;&lt;pre&gt;     mvn cli:execute-phase&lt;/pre&gt;&lt;p&gt;Кстати, если вы меняете pom-файлы, то плагин придется перезапустить.&lt;/p&gt;&lt;p&gt;Последнее, что хотелось бы отметить - это возможность задания алиасов в коммандной строке, они указываются следующим образом:&lt;/p&gt;&lt;pre&gt;  &amp;lt;plugins&amp;gt;&lt;br /&gt;    &amp;lt;plugin&amp;gt;&lt;br /&gt;       &amp;lt;groupId&amp;gt;org.twdata.maven&amp;lt;/groupId&amp;gt;&lt;br /&gt;       &amp;lt;artifactId&amp;gt;maven-cli-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;       &amp;lt;configuration&amp;gt;&lt;br /&gt;           &lt;b&gt;&amp;lt;userAliases&amp;gt;&lt;br /&gt;                 &amp;lt;module-build&amp;gt;module clean install -Dpmd.skip=true -Dcheckstyle.skip=true&amp;lt;/module-build&amp;gt;&lt;br /&gt;                 &amp;lt;web-build&amp;gt;web-module package -Pdevelopment&amp;lt;/web-build&amp;gt;&lt;br /&gt;                 &amp;lt;rebuild&amp;gt;module-build web-build&amp;lt;/rebuild&amp;gt;&lt;br /&gt;           &amp;lt;/userAliases&amp;gt;&lt;/b&gt;&lt;br /&gt;       &amp;lt;/configuration&amp;gt;&lt;br /&gt;    &amp;lt;/plugin&amp;gt;&lt;br /&gt;  &amp;lt;/plugins&amp;gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-2946891598163709962?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/AIUwGTf_gEw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/AIUwGTf_gEw/maven.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2009/05/maven.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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">1</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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">6</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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">7</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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">0</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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">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'/&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 xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/12/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7912032778502117730</guid><pubDate>Thu, 11 Dec 2008 17:26:00 +0000</pubDate><atom:updated>2008-12-11T22:36:02.218+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">event</category><category domain="http://www.blogger.com/atom/ns#">spring</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Event-Driven Development в Spring. Часть 2</title><description>&lt;p&gt;Механизм рассылки event-ов в контексте Spring реализован одним интерфейсом &lt;code&gt;org.springframework.context.event.ApplicationEventMulticaster&lt;/code&gt;. Именно ему и будут делегироваться все добавления, удаления listener-ов, а также публикация event-ов для них. По-умолчанию, в качестве реализации такого интерфейса будет использоваться объект класса &lt;code&gt;org.springframework.context.event.SimpleApplicationEventMulticaster&lt;/code&gt;. В самом же контексте данный bean регистрируется под именем &lt;code&gt;applicationEventMulticaster&lt;/code&gt;. Однако, если под таким именем bean уже существует и реализует нужный интерфейс, то будет использоваться именно он.&lt;/p&gt;&lt;p&gt;Необоходимость создания своего ApplicationEventMulticaster-а может возникнуть, например, чтобы рассылать event-ы по spring-контекстам целого кластера. Именно этот случай и реализован в моем примере. Сам механизм коммуникации между нодами обеспечивается фреймворком &lt;a href="http://www.jgroups.org/"&gt;JGroups&lt;/a&gt;, который рассылает сообщения по сети multicast-ом через UDP-протокол. Объект &lt;code&gt;com.blogspot.nkoksharov.springevents.jgroups.JGroupsMulticaster&lt;/code&gt; переопределяет метод &lt;code&gt;void multicastEvent(ApplicationEvent event)&lt;/code&gt; и рассылает по сети лишь те экземпляры event-ов, классы которых являются подклассами &lt;code&gt;com.blogspot.nkoksharov.springevents.jgroups.JGroupsEvent&lt;/code&gt;. А вот и сам код &lt;code&gt;JGroupsMulticaster&lt;/code&gt;-а:&lt;/p&gt;&lt;pre&gt;package com.blogspot.nkoksharov.springevents.jgroups;&lt;br /&gt;&lt;br /&gt;import org.jgroups.*;&lt;br /&gt;import org.slf4j.*;&lt;br /&gt;import org.springframework.beans.factory.DisposableBean;&lt;br /&gt;import org.springframework.beans.factory.InitializingBean;&lt;br /&gt;import org.springframework.context.ApplicationEvent;&lt;br /&gt;import org.springframework.context.event.SimpleApplicationEventMulticaster;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Alternative multicaster to org.springframework.context.event.SimpleApplicationEventMulticaster&lt;br /&gt; *&lt;br /&gt; * @author nkoksharov&lt;br /&gt; *&lt;br /&gt; */&lt;br /&gt;public class JGroupsMulticaster extends SimpleApplicationEventMulticaster    implements InitializingBean, DisposableBean, LocalEventMulticaster {&lt;br /&gt;&lt;br /&gt;   private final Logger logger = LoggerFactory.getLogger(getClass());&lt;br /&gt;   private JChannel jchannel;&lt;br /&gt;   private JGroupsReceiver receiver = new JGroupsReceiver();&lt;br /&gt;   private String configFile;&lt;br /&gt;   private String clusterName;&lt;br /&gt;&lt;br /&gt;   public void multicastEvent(ApplicationEvent event) {&lt;br /&gt;      if (event instanceof JGroupsEvent) {&lt;br /&gt;          try {&lt;br /&gt;             jchannel.send(new Message(null, null, event));&lt;br /&gt;          } catch (ChannelNotConnectedException e) {&lt;br /&gt;             logger.error("channel not connected", e);&lt;br /&gt;          } catch (ChannelClosedException e) {&lt;br /&gt;             logger.error("channel closed", e);&lt;br /&gt;          }&lt;br /&gt;      } else {&lt;br /&gt;          super.multicastEvent(event);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void localMulticastEvent(JGroupsEvent event) {&lt;br /&gt;      super.multicastEvent(event);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void afterPropertiesSet() throws Exception {&lt;br /&gt;      jchannel = new JChannel(configFile);&lt;br /&gt;      receiver.setLocalMulticater(this);&lt;br /&gt;      jchannel.setReceiver(receiver);&lt;br /&gt;      jchannel.connect(clusterName);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void destroy() throws Exception {&lt;br /&gt;      jchannel.close();&lt;br /&gt;   }&lt;br /&gt;   public void setConfigFile(String configFile) {&lt;br /&gt;      this.configFile = configFile;&lt;br /&gt;   }&lt;br /&gt;   public void setClusterName(String clusterName) {&lt;br /&gt;      this.clusterName = clusterName;&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Рассылка же абсолютно всех event-ов, в частности системных, может привести к некорректной работе всего spring-контекста.&lt;/p&gt;&lt;div style="text-align:center;"&gt;&lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/aSS5J6X64r3zILab7jnrAJ/download?filename=bean2.png"/&gt;&lt;/div&gt;&lt;p&gt;Проект собирается с помощью &lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt;-а и запускается командой:&lt;/p&gt;&lt;pre&gt;mvn compile exec:java&lt;/pre&gt;&lt;p&gt;Каждый запуск - это новый контекст, т.е. можно запустить приложение на разных компьютерах в локальной сети и наблюдать появление сообщений в консоли о получении event-ов. Также можно запустить несколько экземпляров приложения на одном компьютере, все должно работать.&lt;/p&gt;&lt;a href="http://www.assembla.com/spaces/nkoksharov/documents/ahcQIAX4Wr3BIhab7jnrAJ/download?filename=springevents.zip"&gt;Скачать исходники&lt;/a&gt;&lt;br /&gt;&lt;a href="http://nkoksharov.blogspot.com/2008/10/event-driven-development-spring-1.html"&gt;Event-Driven Development в Spring. Часть 1&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-7912032778502117730?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/JctI9re7t_I" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/JctI9re7t_I/event-driven-development-spring-2.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/12/event-driven-development-spring-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-5747000202578883787</guid><pubDate>Mon, 01 Dec 2008 17:17:00 +0000</pubDate><atom:updated>2008-12-11T22:10:07.652+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">tool</category><category domain="http://www.blogger.com/atom/ns#">expression</category><category domain="http://www.blogger.com/atom/ns#">benchmark</category><title>Rhino и Janino для вычисления математических выражений</title><description>&lt;p&gt;Для вычисления математических формул в run-time можно воспользоваться script-движком реализующим &lt;a href="https://scripting.dev.java.net/"&gt;JSR-223&lt;/a&gt;. В Java 1.6 реализация этой спецификации представлена javascript-движоком &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt;. Однако применение целого script-движка для обычного вычисления формул будет довольно неоправданным решением. Мой поиск привел к &lt;a href="http://www.janino.net/"&gt;Janino&lt;/a&gt; - это целый фреймворк для динамической компиляции и исполнения java. То есть он позволяет выполнять скрипты на java внутри самой java. Одно из его призваний это компиляция и выполнение различных математических выражений с помощью &lt;code&gt;org.codehaus.janino.ExpressionEvaluator&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Я решил сравнить производительность Rhino и Janino. Для теста выбрал простую формулу: &lt;code&gt;(x-10)*Math.sin(0.1)*2&lt;/code&gt;, вычисление которой будет осуществляться 1000000 раз.&lt;/p&gt;&lt;pre&gt;package com.blogspot.nkoksharov;&lt;br /&gt;&lt;br /&gt;import javax.script.*;&lt;br /&gt;&lt;br /&gt;import org.apache.commons.lang.time.StopWatch;&lt;br /&gt;import org.codehaus.janino.ExpressionEvaluator;&lt;br /&gt;import org.mozilla.javascript.Context;&lt;br /&gt;import org.mozilla.javascript.ContextFactory;&lt;br /&gt;import org.mozilla.javascript.Function;&lt;br /&gt;import org.mozilla.javascript.Scriptable;&lt;br /&gt;import org.testng.annotations.Test;&lt;br /&gt;&lt;br /&gt;public class ExpressionTest {&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testRhino() throws ScriptException {&lt;br /&gt;        ScriptEngineManager scriptManager = new ScriptEngineManager();&lt;br /&gt;        ScriptEngine se = scriptManager.getEngineByName("JavaScript");&lt;br /&gt;        SimpleBindings bindings = new SimpleBindings();&lt;br /&gt;        bindings.put("x", 2);&lt;br /&gt;&lt;br /&gt;        StopWatch stopWatch = new StopWatch();&lt;br /&gt;        stopWatch.start();&lt;br /&gt;        for (int i = 0; i &lt; 1000000; i++) {&lt;br /&gt;            Double result = (Double) se.eval("(x-10)*Math.sin(0.1)*2", bindings);&lt;br /&gt;        }&lt;br /&gt;        stopWatch.stop();&lt;br /&gt;        System.out.println("JDK Rhino time: " + stopWatch);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testRhinoFunc() throws ScriptException, NoSuchMethodException {&lt;br /&gt;        ScriptEngineManager scriptManager = new ScriptEngineManager();&lt;br /&gt;        ScriptEngine se = scriptManager.getEngineByName("JavaScript");&lt;br /&gt;&lt;br /&gt;        String script = "function count(x) { return (x-10)*Math.sin(0.1)*2; }";&lt;br /&gt;        se.eval(script);&lt;br /&gt;&lt;br /&gt;        StopWatch stopWatch = new StopWatch();&lt;br /&gt;        stopWatch.start();&lt;br /&gt;        for (int i = 0; i &lt; 1000000; i++) {&lt;br /&gt;            Invocable inv = (Invocable) se;&lt;br /&gt;            Double result = (Double) inv.invokeFunction("count", 2);&lt;br /&gt;        }&lt;br /&gt;        stopWatch.stop();&lt;br /&gt;        System.out.println("JDK Rhino func time: " + stopWatch);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testNativeRhinoCompiledFunc() {&lt;br /&gt;        ContextFactory cf = new ContextFactory();&lt;br /&gt;        Context cx = cf.enterContext();&lt;br /&gt;        Scriptable scope = cx.initStandardObjects();&lt;br /&gt;        Function f = cx.compileFunction(scope, "function count(x) { return (x-10)*Math.sin(0.1)*2; }", null, 0, null);&lt;br /&gt;&lt;br /&gt;        StopWatch stopWatch = new StopWatch();&lt;br /&gt;        stopWatch.start();&lt;br /&gt;        for (int i = 0; i &lt; 1000000; i++) {&lt;br /&gt;            Double value = (Double) f.call(cx, scope, scope, new Object[] {2});&lt;br /&gt;        }&lt;br /&gt;        stopWatch.stop();&lt;br /&gt;        System.out.println("Mozilla Rhino compiled-func time: " + stopWatch);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testJanino() throws Exception {&lt;br /&gt;        ExpressionEvaluator ee = new ExpressionEvaluator("(x-10)*Math.sin(0.1)*2",&lt;br /&gt;            Double.class, new String[] {"x"}, new Class[] {Integer.class});&lt;br /&gt;&lt;br /&gt;        StopWatch stopWatch = new StopWatch();&lt;br /&gt;        stopWatch.start();&lt;br /&gt;        for (int i = 0; i &lt; 1000000; i++) {&lt;br /&gt;            Double result = (Double) ee.evaluate(new Object[] {2});&lt;br /&gt;        }&lt;br /&gt;        stopWatch.stop();&lt;br /&gt;        System.out.println("Janino time: " + stopWatch);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Результат вполне оправдал мои ожидания:&lt;/p&gt;&lt;pre&gt;                                 (ч:мм:сс.мс)&lt;br /&gt;                  JDK Rhino time: 0:01:50.061&lt;br /&gt;             JDK Rhino func time: 0:00:59.040&lt;br /&gt;Mozilla Rhino compiled-func time: 0:00:00.344&lt;br /&gt;                     Janino time: 0:00:00.156&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;В итоге получается, что скорость выполнения с Rhino использующейся в JDK заметно отстает от Janino, даже не смотря на выполнение через javascript-функцию. Однако, если вы будете использовать реализацию Rhino от Mozilla, с возможностью компиляции в bytecode, то получите лишь 2-х кратное отставание по скорости. И так первенство у Janino, который также компилирует выражения перед выполнением. При этом обращения к методу &lt;code&gt;ExpressionEvaluator.evaluate&lt;/code&gt; будут thread-safe.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-5747000202578883787?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/ucuwr9vBZfU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/ucuwr9vBZfU/rhino-janino.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/12/rhino-janino.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-8397199073283619705</guid><pubDate>Wed, 19 Nov 2008 19:52:00 +0000</pubDate><atom:updated>2008-12-01T19:15:44.942+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">tool</category><category domain="http://www.blogger.com/atom/ns#">bytecode</category><title>Генерация javabean в runtime</title><description>&lt;p&gt;Если требуется создать javabean в runtime по набору полей, то никакими стандартными средствами java эту задачу решить не удастся.&lt;/p&gt;&lt;p&gt;По началу я решил, что с этой задачей может справиться какая-либо из реализаций &lt;code&gt;org.apache.commons.beanutils.DynaBean&lt;/code&gt;. Однако работать с ним можно лишь так &lt;code&gt;if (object instanceof DynaBean) ... &lt;/code&gt;. То есть, если модуль использующий этот объект работает с ним только через reflection и ничего не знает о &lt;code&gt;DynaBean&lt;/code&gt;, то такое решение не подходит.&lt;/p&gt;&lt;p&gt;В итоге, было решено использовать &lt;a href="http://www.csg.is.titech.ac.jp/~chiba/javassist/"&gt;Javassist&lt;/a&gt;. Очень удобный инструмент для подобного рода действий, позволяет производить как создание так и модификацию уже загруженных JVM классов. Предоставляет два вида API, на уровне исходного кода и собственно самого байткода. Использовать конечно лучше первый, т.к. мало кого устроит работать напрямую с байткодом.&lt;/p&gt;&lt;p&gt;В альтернативу classloader-у используется класс &lt;code&gt;javassist.ClassPool&lt;/code&gt;, он позволяет не только загружать, но и создавать классы &lt;code&gt;javassist.CtClass&lt;/code&gt;. При этом класс не будет доступен приложению сразу же, а лишь после преобразования в обычный класс &lt;code&gt;java.lang.Class&lt;/code&gt;. Сам &lt;code&gt;javassist.CtClass&lt;/code&gt; обладает богатыми возможностями для создания, удаления методов и полей, изменения наследования или даже названия уже загруженного класса. Для доступа ко всем классам загруженным JVM следует использовать &lt;code&gt;ClassPool.getDefault()&lt;/code&gt;, хотя можно реализовать и свой, чтобы, например, загружать классы самостоятельно.&lt;/p&gt;&lt;p&gt;Итак, решение с использованием Javassist будет выглядеть так:&lt;/p&gt;&lt;pre&gt;public Class&amp;lt;?&amp;gt; createClass(String className, Map&amp;lt;String, Class&amp;lt;?&amp;gt;&amp;gt; props) throws Exception {&lt;br /&gt; ClassPool classPool = ClassPool.getDefault();&lt;br /&gt; CtClass cc = classPool.makeClass(className);&lt;br /&gt; &lt;br /&gt; for (Entry&amp;lt;String, Class&amp;lt;?&amp;gt;&amp;gt; entry : props.entrySet()) {&lt;br /&gt;  String name = entry.getKey();&lt;br /&gt;  Class&amp;lt;?&amp;gt; type = entry.getValue();&lt;br /&gt;  CtClass fieldType = classPool.get(type.getName());&lt;br /&gt;  CtField field = new CtField(fieldType, name, cc);&lt;br /&gt;  cc.addField(field);&lt;br /&gt;  &lt;br /&gt;  String n = camelize(name);&lt;br /&gt;  CtMethod setter = CtNewMethod.setter(&amp;quot;set&amp;quot; + n, field);&lt;br /&gt;  cc.addMethod(setter);&lt;br /&gt;  CtMethod getter = CtNewMethod.getter(&amp;quot;get&amp;quot; + n, field);&lt;br /&gt;  cc.addMethod(getter);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; return cc.toClass();&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;private String camelize(String s) {&lt;br /&gt; return s.substring(0, 1).toUpperCase() + s.substring(1);&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-8397199073283619705?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/uBEMceqjiGY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/uBEMceqjiGY/javabean-runtime.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/11/javabean-runtime.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-3725305119060525649</guid><pubDate>Sun, 16 Nov 2008 15:59:00 +0000</pubDate><atom:updated>2008-11-17T11:58:12.784+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">sql</category><category domain="http://www.blogger.com/atom/ns#">tool</category><category domain="http://www.blogger.com/atom/ns#">logging</category><title>Log4jdbc логгер для sql-запросов</title><description>&lt;p&gt;&lt;a href="http://log4jdbc.sourceforge.net/"&gt;Log4jdbc&lt;/a&gt; представляет собой jdbc-драйвер, который может логировать sql-вызовы используя &lt;a href="http://nkoksharov.blogspot.com/2008/11/slf4j-commons-logging.html"&gt;SLF4J&lt;/a&gt;. Реализует как JDBC 3 так и JDBC 4 спецификации. Для настройки к вашему jdbc-url необходимо просто добавить префикс &lt;code&gt;jdbc:log4&lt;/code&gt;. Например:&lt;/p&gt;&lt;p&gt;&lt;code&gt;jdbc:postgresql://localhost:5432/test&lt;/code&gt;, заменить на &lt;code&gt;jdbc:log4jdbc:postgresql://localhost:5432/test&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Затем в log4j.xml или log4j.properties настроить logger на любую из категорий:&lt;ul&gt;&lt;li&gt;&lt;p&gt;jdbc.sqlonly - логирование только sql запросов,&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;jdbc.sqltiming - логирование sql-запросов и их времени выполнения,&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;jdbc.audit - логирование всех jdbc-операций за исключением ResultSet-ов,&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;jdbc.resultset - самое полное логирование, включающее в себя операции со всеми ResultSet-объектами,&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;jdbc.connection - логирование операций с jdbc-соединениями&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-3725305119060525649?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/Owak_7XVU2s" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/Owak_7XVU2s/log4jdbc-sql.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/11/log4jdbc-sql.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-2883297616878382413</guid><pubDate>Wed, 12 Nov 2008 04:20:00 +0000</pubDate><atom:updated>2008-11-14T10:56:21.036+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">http</category><category domain="http://www.blogger.com/atom/ns#">cache</category><title>Отключение кэширования html-страницы. Часть 2</title><description>&lt;p&gt;Использование meta-тэгов, описываемых в &lt;a href="http://nkoksharov.blogspot.com/2008/10/html.html"&gt;первой части&lt;/a&gt; помогает нам избавиться от кэширования в браузере. Однако, помимо кэша браузера существует кэш прокси-сервера, который проводит анализ лишь HTTP-заголовков. К тому же может появится необходимость отключить кэширование не только самой html-страницы, но и ресурсов, находящихся на ней (jpg, swf ...). Данную проблему можно решить таким вот servlet-фильтром:&lt;/p&gt;&lt;pre&gt;package ru.someone;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.util.*;&lt;br /&gt;import javax.servlet.*;&lt;br /&gt;import org.slf4j.*;&lt;br /&gt;&lt;br /&gt;public class ResponseHeaderFilter implements Filter {&lt;br /&gt;&lt;br /&gt;    private final Logger log = LoggerFactory.getLogger(getClass());&lt;br /&gt;    private final Map&lt;String, String&gt; httpParams = new HashMap&lt;String, String&gt;();&lt;br /&gt;&lt;br /&gt;    public void init(FilterConfig filterConfig) throws ServletException {&lt;br /&gt;        Enumeration&lt;String&gt; enums = filterConfig.getInitParameterNames();&lt;br /&gt;        while(enums.hasMoreElements()) {&lt;br /&gt;            final String name = enums.nextElement();&lt;br /&gt;            final String value = filterConfig.getInitParameter(name);&lt;br /&gt;            log.debug("HTTP-header registred - {}:{}", name, value);&lt;br /&gt;            httpParams.put(name, value);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)&lt;br /&gt;            throws IOException, ServletException {&lt;br /&gt;        HttpServletResponse httpResponse = (HttpServletResponse) response;&lt;br /&gt;        for (Entry&lt;String, String&gt; entry : httpParams.entrySet()) {&lt;br /&gt;            httpResponse.setHeader(entry.getKey(), entry.getValue());&lt;br /&gt;        }&lt;br /&gt;        chain.doFilter(request, response);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void destroy() {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Этот фильтр выставляет HTTP-заголовки, объекту &lt;code&gt;javax.servlet.HttpServletResponse&lt;/code&gt;, которые берет из конфигурации фильтра. Соответственно в самом web.xml нужно написать:&lt;/p&gt;&lt;pre&gt;&amp;lt;filter&amp;gt;&lt;br /&gt;     &amp;lt;filter-name&amp;gt;responseHeaderFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;        &amp;lt;filter-class&amp;gt;ru.someone.ResponseHeaderFilter&amp;lt;/filter-class&amp;gt;&lt;br /&gt;        &amp;lt;init-param&amp;gt;&lt;br /&gt;            &amp;lt;param-name&amp;gt;Cache-Control&amp;lt;/param-name&amp;gt;&lt;br /&gt;            &amp;lt;param-value&amp;gt;no-store, max-age=0, must-revalidate&amp;lt;/param-value&amp;gt;&lt;br /&gt;        &amp;lt;/init-param&amp;gt;&lt;br /&gt;        &amp;lt;init-param&amp;gt;&lt;br /&gt;            &amp;lt;param-name&amp;gt;Pragma&amp;lt;/param-name&amp;gt;&lt;br /&gt;            &amp;lt;param-value&amp;gt;no-cache&amp;lt;/param-value&amp;gt;&lt;br /&gt;        &amp;lt;/init-param&amp;gt;&lt;br /&gt;        &amp;lt;init-param&amp;gt;&lt;br /&gt;            &amp;lt;param-name&amp;gt;Expires&amp;lt;/param-name&amp;gt;&lt;br /&gt;            &amp;lt;param-value&amp;gt;Fri, 01 Jan 1990 00:00:00 GMT&amp;lt;/param-value&amp;gt;&lt;br /&gt;        &amp;lt;/init-param&amp;gt;&lt;br /&gt;&amp;lt;/filter&amp;gt;&lt;br /&gt;&amp;lt;filter-mapping&amp;gt;&lt;br /&gt;     &amp;lt;filter-name&amp;gt;responseHeaderFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;     &amp;lt;url-pattern&amp;gt;*.jpg&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;&amp;lt;filter-mapping&amp;gt;&lt;br /&gt;     &amp;lt;filter-name&amp;gt;responseHeaderFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;     &amp;lt;url-pattern&amp;gt;*.swf&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-2883297616878382413?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/EG6FTdr1EaM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/EG6FTdr1EaM/html-2.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/11/html-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7156769710294268368</guid><pubDate>Sun, 09 Nov 2008 19:00:00 +0000</pubDate><atom:updated>2008-11-25T17:51:46.213+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>Slf4j на смену commons-logging</title><description>&lt;p&gt;Наверное каждый из вас знаком с commons-logging. Однако пользы от него не так много. Наличие трюков с class-loader-ами, а также излишнее использование reflection-а и отсутствие возможности использования параметризированных сообщений, мягко говоря огорчают.&lt;/p&gt;&lt;p&gt;Довольно популярной заменой commons-logging является &lt;a href="http://www.slf4j.org/"&gt;Slf4j&lt;/a&gt; (Simple Logging Framework for Java). Во-первых, в нем реализована поддержка &lt;a href="http://wiki.apache.org/logging-log4j/NDCvsMDC"&gt;MDC&lt;/a&gt; (пока что такая возможность реализована в &lt;a href="http://logback.qos.ch/"&gt;logback&lt;/a&gt; и &lt;a href="http://logging.apache.org/"&gt;log4j&lt;/a&gt;). Этот механизм удобно использовать, когда у вас в контексте одного потока в лог записывается много параметра, которые действительны только для данного контекста. Например, для каждого сообщения необходимо выводить также имя пользователя, его логин и т.д. Вместо того чтобы указывать эти параметры каждый раз в сообщении. Можно положить их в MDC-контекст:&lt;/p&gt;&lt;pre&gt;MDC.put("ipAddress", request.getRemoteAddr());&lt;br /&gt;Principal user = request.getUserPrincipal();&lt;br /&gt;MDC.put("user", user.getName());&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;И указать в &lt;code&gt;ConversionPattern&lt;/code&gt; log4j эти параметры: &lt;/p&gt;&lt;pre&gt;%X{ipAddress} - %X{user} %n%m&lt;/pre&gt;&lt;p&gt;Во-вторых, есть наличие параметризованных сообщений. То есть, если раньше надо было писать так:&lt;/p&gt;&lt;pre&gt;logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));&lt;/pre&gt;&lt;p&gt;Если у нас будет не debug уровень, то будет тратиться время на сборку строки, которая не будет выводиться. Потому надо будет написать так:&lt;/p&gt;&lt;pre&gt;if(logger.isDebugEnabled()) { &lt;br /&gt;     logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Используя Slf4j все это можно заменить одной строкой:&lt;/p&gt;&lt;pre&gt;logger.debug("Value {} was inserted between {} and {}.", &lt;br /&gt;     new Object[] {newVal, below, above});&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Символами &lt;code&gt;{}&lt;/code&gt; как раз и обозначаются параметры строки, которые передаются последним аргументом. При этом сборка строки не будет производиться при уровне ниже чем debug.&lt;/p&gt;&lt;p&gt;В-третьих, сама архитектура представляет собой два модуля, api &lt;code&gt;slf4j-api.jar&lt;/code&gt; и реализация &lt;code&gt;slf4j-XXXXX.jar&lt;/code&gt;. Реализация представлена 6-ью модулями:&lt;/p&gt; &lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/aXAxjARPWr3ALwab7jnrAJ/download/slf4j.png"/&gt;&lt;p&gt;На главной странице этого проекта перечислены проекты, которые используют данный фреймворк и их число растет.&lt;/p&gt;&lt;p&gt;Пример использования:&lt;pre&gt;import org.slf4j.Logger;&lt;br /&gt;import org.slf4j.LoggerFactory;&lt;br /&gt;&lt;br /&gt;class TestClass {&lt;br /&gt;&lt;br /&gt; private final Logger log = LoggerFactory.getLogger( TestClass.class ); &lt;br /&gt;&lt;br /&gt; ...&lt;br /&gt;&lt;br /&gt; log.debug( "..." );&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-7156769710294268368?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/ltlILmLB1iE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/ltlILmLB1iE/slf4j-commons-logging.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/11/slf4j-commons-logging.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-5628276523813952924</guid><pubDate>Tue, 04 Nov 2008 13:00:00 +0000</pubDate><atom:updated>2008-11-09T11:20:56.368+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">instrumentation api</category><category domain="http://www.blogger.com/atom/ns#">bytecode</category><category domain="http://www.blogger.com/atom/ns#">jvm</category><title>Кто такой javaagent?</title><description>&lt;p&gt;У Java-машины есть один интересный параметр &lt;code&gt;-javaagent&lt;/code&gt;. О нем почему-то весьма мало сказано в документации по самой JVM, есть лишь описание в javadoc &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/instrument/package-summary.html"&gt;java.lang.instrument&lt;/a&gt;. Сам параметр появился начиная с Java 1.5, и позволяет получать доступ к механизму манипулирования с байт-кодом классов (&lt;b&gt;трансформация&lt;/b&gt;, &lt;b&gt;переопределение&lt;/b&gt; классов).&lt;/p&gt;&lt;p&gt;В командной строке он выглядит так:&lt;/p&gt;&lt;pre&gt;    -javaagent:&lt;i&gt;jarpath[=options]&lt;/i&gt;&lt;/pre&gt;&lt;p&gt;где &lt;code&gt;jarpath&lt;/code&gt; - путь к jar-нику содержащему класс agent-а, и &lt;code&gt;options&lt;/code&gt; - строка доп. параметров agent-а, передается при его вызове.&lt;/p&gt;&lt;p&gt;Сам параметер может содержаться несколько раз в строке параметров JVM, позволяя загружать несколько agent-ов. Указываемый JAR должен удовлетворять &lt;a href="http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html"&gt;спецификации JAR-файла&lt;/a&gt;, т.е. иметь манифест файл META-INF/MANIFEST.MF, со следующими возможными атрибутами:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Premain-Class&lt;/code&gt; (обязательный) - он содержит имя класса agent-а с &lt;code&gt;premain&lt;/code&gt; методом.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Boot-Class-Path&lt;/code&gt; - содержит как абсолютные так и относительные пути поиска классов, для Bootstrap classloader-а. Относительный путь начинается от абсолютного пути самого JAR-файла. Пути могут указывать как на директорию, так и на jar-библиотеку. В качестве разделителя используются пробел. Эти пути будут использоваться в случае, если какие-то классы не были найдены стандартными механизмами Java.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Can-Redefine-Classes&lt;/code&gt; - указывает возможность переопределения классов, может иметь значение &lt;code&gt;true&lt;/code&gt;, либо &lt;code&gt;false&lt;/code&gt; (по-умолчанию).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Can-Retransform-Classes&lt;/code&gt; (только в Java 1.6) - указывает возможность ретрансформации классов, может иметь значение &lt;code&gt;true&lt;/code&gt;, либо &lt;code&gt;false&lt;/code&gt; (по-умолчанию).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Метод &lt;code&gt;premain&lt;/code&gt;, может иметь одну из следующих сигнатур:&lt;/p&gt;&lt;pre&gt;public static void premain(String agentArgs, Instrumentation inst);&lt;/pre&gt;&lt;p&gt;&lt;code&gt;public static void premain(String agentArgs);&lt;/code&gt; (только Java 1.6)&lt;/p&gt;&lt;p&gt;В &lt;code&gt;agentArgs&lt;/code&gt; передается сама строка &lt;code&gt;options&lt;/code&gt;, разработчик должен сам реализовать логику парсинга этой строки, если хочет передавать в ней несколько аргументов. Собственно сам интерфейс &lt;code&gt;java.lang.instrument.Instrumentation&lt;/code&gt; и предоставляет нам доступ к механизмам операций с байт-кодом. Сам метод &lt;code&gt;premain&lt;/code&gt; вызывается, как вы уже должно быть поняли, еще до выполнения метода &lt;code&gt;main&lt;/code&gt; приложения. &lt;/p&gt;&lt;pre&gt;public class InstrumentExample {&lt;br /&gt;&lt;br /&gt;    private static Instrumentation inst;&lt;br /&gt;&lt;br /&gt;    public static void premain(String options, Instrumentation inst) {&lt;br /&gt;        inst.addTransformer(new SomeTransformer());&lt;br /&gt;        this.inst = inst;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String args[] ) {&lt;br /&gt;     &lt;br /&gt;        ...&lt;br /&gt;&lt;br /&gt;        byte[] &lt;b&gt;classBytes&lt;/b&gt; = ...&lt;br /&gt;        ClassDefinition classDef = new ClassDefinition(SomeClass.class, &lt;b&gt;classBytes&lt;/b&gt;);&lt;br /&gt;        inst.redefineClasses(classDef);&lt;br /&gt;&lt;br /&gt;        ...&lt;br /&gt;        &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static class SomeTransformer implements ClassFileTransformer {&lt;br /&gt;        public byte[] transform(java.lang.ClassLoader loader,&lt;br /&gt;                java.lang.String className,&lt;br /&gt;                java.lang.Class classBeingRedefined,&lt;br /&gt;                java.security.ProtectionDomain protectionDomain,&lt;br /&gt;                byte[] &lt;b&gt;classfileBuffer&lt;/b&gt;) throws IllegalClassFormatException&lt;br /&gt;        {&lt;br /&gt;            ...&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Чтобы использовать механизм &lt;b&gt;трансфорамации&lt;/b&gt; классов вам нужно реализовать интерфейс &lt;code&gt;java.lang.instrument.ClassFileTransformer&lt;/code&gt;. И зарегистрировать свою реализацию через метод &lt;code&gt;Instrumentation.addTransformer&lt;/code&gt;. Сам трасформер будет срабатывать каждый раз при:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;загрузке класса &lt;code&gt;ClassLoader.defineClass&lt;/code&gt;&lt;/li&gt;&lt;li&gt;переопределении класса &lt;code&gt;Instrumentation.redefineClasses&lt;/code&gt; &lt;/li&gt;&lt;li&gt;ретрансформации класса &lt;code&gt;Instrumentation.retransformClasses&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Аргумент &lt;code&gt;classfileBuffer&lt;/code&gt; содержит байт-код текущей версии класса, и его нельзя модифицировать, для его переопределения трансформер должен вернуть новый массив байт, либо &lt;code&gt;null&lt;/code&gt;, если трансформация не была произведена. В случае, когда трансформеров несколько, то они будут вызваны по цепочке, при этом &lt;code&gt;classfileBuffer&lt;/code&gt; будет результатом предыдущего трансформера. Чтобы сообщить об ошибке при трансформации необходимо выбросить &lt;code&gt;java.lang.instrument.IllegalClassFormatException&lt;/code&gt;, при unchecked-ошибках результат будет такой, как если бы метод вернул &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Для &lt;b&gt;переопределения&lt;/b&gt; класса необходимо указать выше описанный параметер в манифесте и использовать класс &lt;code&gt;java.lang.instrument.ClassDefinition&lt;/code&gt;. Только вот есть ограничения: переопределение класса не должно добавлять, удалять новые поля или методы, менять сигнатуру методов или иерархию наследования. Можно менять только тело методов, структура класса меняться не должна. Если в приложении уже используются экземпляры предыдущей версии класса, то с ними ничего не произойдет, однако при последующем создании класса, будет использована уже новая версия.&lt;/p&gt;&lt;p&gt;К сожалению, Java-платформа не предоставляет стандартных инструментов для модификации/генерации байт-кода. В этом случае можно воспользоваться уже готовыми фреймворками, например, &lt;a href="http://www.csg.is.titech.ac.jp/~chiba/javassist/"&gt;Javaassist&lt;/a&gt; или &lt;a href="http://jakarta.apache.org/bcel/"&gt;BCEL&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Javaаgent реализован в фреймворке &lt;a href="http://static.springframework.org/spring/docs/2.5.x/reference/aop.html#aop-aj-ltw-environment-generic"&gt;Spring&lt;/a&gt;, чтобы можно было использовать AOP для классов, вне контекста. Тестовый фреймворк &lt;a href="https://jmockit.dev.java.net/"&gt;Jmockit&lt;/a&gt; также реализует своего javaagent-а. Тем самым открывая возможность писать mock-и для классов, экземпляры которых вы не можете заменить обычными способами, либо они содержат статические методы, заменить которые возможно только до загрузки класса.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/757496094257544869-5628276523813952924?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/zVFnr6nCQuY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/zVFnr6nCQuY/javaagent.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/11/javaagent.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7530026150612637056</guid><pubDate>Fri, 31 Oct 2008 13:52:00 +0000</pubDate><atom:updated>2008-11-02T18:34:00.348+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">event</category><category domain="http://www.blogger.com/atom/ns#">spring</category><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Event-Driven Development в Spring. Часть 1</title><description>&lt;p&gt;Еще в Spring 1.2 была возможность посылать различные event-ы через весь контекст приложения. Я считаю этот вид коммуникации в некоторых местах приложения единственным верным решением.&lt;/p&gt;&lt;p&gt;Опишу кратко как работает этот механизм. Базовым классом для event-а должен быть &lt;code&gt;org.springframework.context.ApplicationEvent&lt;/code&gt;, для рассылки которого используется метод &lt;code&gt;org.springframework.context.ApplicationContext.publishEvent(event)&lt;/code&gt;. Ну а получать event-ы будут те bean-ы, которые реализуют интерфейс &lt;code&gt;org.springframework.context.ApplicationListener&lt;/code&gt; с единственным методом &lt;code&gt;onApplicationEvent(event)&lt;/code&gt;. &lt;/p&gt;&lt;div style="text-align:center;"&gt;&lt;img src="http://www.assembla.com/spaces/nkoksharov/documents/beCDyQQpmr3AwJab7jnrAJ/download/applicationlisteners.png" border="0" alt=""/&gt;&lt;/div&gt;&lt;p&gt;Допустим, что у нас есть логика, после выполнения которой другим сервисам необоходимо также сделать какие-то действия. Стандартный вариант решения такой задачи предполагает использование IoC:&lt;/p&gt;&lt;pre&gt;public class BussinesServiceImpl implements BussinesService {&lt;br /&gt;&lt;br /&gt;    @Autowired&lt;br /&gt;    private EmailService emailService;&lt;br /&gt;    @Autowired&lt;br /&gt;    private LoggingService logService;&lt;br /&gt;    &lt;br /&gt;    public void doAction() {&lt;br /&gt;        &lt;br /&gt;        ...&lt;br /&gt;        &lt;br /&gt;        emailService.sendEmail(email, body);&lt;br /&gt;        logService.recordLog(data);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Также можно использовать и аспекты. Только вот получать параметры, которые пришли не через аргументы метода, advice-ам будет сложно. Реализовать такой случай с помощью event-ов можно так:&lt;/p&gt;&lt;pre&gt;public class BussinesServiceImpl implements BussinesService, ApplicationContextAware {&lt;br /&gt;&lt;br /&gt;    private ApplicationContext context;&lt;br /&gt;    &lt;br /&gt;    public void doAction() {&lt;br /&gt;        &lt;br /&gt;        ...&lt;br /&gt;        &lt;br /&gt;        ActionEvent event = new ActionEvent();&lt;br /&gt;        event.setEmail(email);&lt;br /&gt;        event.setEmailBody(body);&lt;br /&gt;        event.setData(data);&lt;br /&gt;        context.publishEvent(event);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void setApplicationContext(ApplicationContext value) {&lt;br /&gt;        context = value;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Соответственно сами сервисы будут выглядеть так:&lt;/p&gt;&lt;pre&gt;public class EmailServiceImpl implements EmailService, ApplicationListener {&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    public void onApplicationEvent(ApplicationEvent event) {&lt;br /&gt;        if (!(event instanceof ActionEvent)) {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        ActionEvent actionEvent = (ActionEvent) event;&lt;br /&gt;        sendEmail(actionEvent.getEmail(), actionEvent.getEmailBody());&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class LoggingServiceImpl implements LoggingService, ApplicationListener {&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    public void onApplicationEvent(ApplicationEvent event) {&lt;br /&gt;        if (!(event instanceof ActionEvent)) {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        ActionEvent actionEvent = (ActionEvent) event;&lt;br /&gt;        recordLog(actionEvent.getData());&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Какое преимущество дает такой подход? Изоляцию сервиса, от лишних зависимостей. Такая изоляция дает возможность выносить сервисы типа &lt;code&gt;BussinesService&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-7530026150612637056?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/r26cVCbqqq4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/r26cVCbqqq4/event-driven-development-spring-1.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/10/event-driven-development-spring-1.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-4845066644512041646</guid><pubDate>Wed, 29 Oct 2008 19:23:00 +0000</pubDate><atom:updated>2008-10-31T10:42:46.502+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">applet</category><category domain="http://www.blogger.com/atom/ns#">network</category><category domain="http://www.blogger.com/atom/ns#">spring security</category><category domain="http://www.blogger.com/atom/ns#">browser</category><title>Идентификация пользователя в браузере. Как узнать IP и MAC в Java-апплете.</title><description>&lt;p&gt;Задача: необходимо чтобы пользователь, с одного компьютера, имел лишь одну сессию с сервером.&lt;/p&gt;&lt;p&gt;При этом нужно учитывать, что пользователь может использовать разные браузеры, так что зацепка за какой-нибудь параметр http-заголовока типа &lt;code&gt;User-agent&lt;/code&gt; ничего не даст. Однако возможно получить реальный ip-адрес клиента в подсети, а не возможного прокси, который мы получаем через &lt;code&gt;HttpServletRequest.getLocalAddr()&lt;/code&gt;. Редко сейчас у клиентов есть выделенные ip. Также, помимо ip можно получить и mac-адрес! Все что для этого нужно так это написать Java-Applet, который запустится у клиента в браузере и передаст нужную нам информацию.&lt;/p&gt;&lt;p&gt;Вообще возможность определение ip адреса во внутренней сети пришла в голову некому &lt;a href="http://reglos.de/lars/"&gt;Lars Kindermann-у&lt;/a&gt; в 2002-ом году. Судя по фотке он проживает где-то на полярном круге .-) Его идея оказалась принципиально простой. Java-Applet создает подключение к серверу, с которого его скачали, через объект &lt;code&gt;java.net.Socket&lt;/code&gt;. В результате мы легко можем получить сетевую информацию о клиенте.&lt;/p&gt;&lt;p&gt;Ну а чтобы получить mac-адрес, не придется даже создавать соединения, можно просто сделать вызов &lt;code&gt;java.net.NetworkInterface.getHardwareAddress()&lt;/code&gt;. Можно было бы довольствоваться лишь mac адресом. Однако, возможность его получения появилась лишь в JRE 1.6.&lt;/p&gt;&lt;p&gt;В решении я использовал, как обычно, Spring Security 2.0.4. Идея следующая, после аутентификации пользователя мы проверяем есть ли такой же клиент, с тем же ip и mac-ом, уже в системе. Для этого необходимо, чтобы фильтр аутентификации перебросил нас на страничку с апплетом. Затем уже апплет собирает сетевую информацию о клиенте и отсылает ее на сервер простым GET-ом, в ответ на этот запрос, сервер, выполнив проверку, посылает редирект на следующую страничку, либо выдает ошибку. При получении запроса сервером, составляется композитный id клиента, в нем учавствует как mac и локальный ip клиента, пришедшие от апплета, так и ip шлюза с которым соединен сервер и за которым находится сам клиент. Я свел воедино оба решения, использовать нужно либо ip+ip шлюза, либо mac вместе их использовать уже избыточно. Если есть возможность, и в требованиях вашего приложения предусмотрена возможность установки JRE 1.6 на машину клиента, то лучше использовать mac. Способ идентификации основанный только на ip имеет недостаток. В случае если за одним шлюзом может находится несколько подсетей, а следовательно и клиентов с одинаковым адресом может быть тоже несколько.&lt;/p&gt;&lt;p&gt;Вот собственно и сам исходный код апплета:&lt;/p&gt;&lt;pre&gt;import java.applet.Applet;&lt;br /&gt;import java.net.NetworkInterface;&lt;br /&gt;import java.net.Socket;&lt;br /&gt;import java.net.SocketException;&lt;br /&gt;import java.net.URL;&lt;br /&gt;import java.util.Enumeration;&lt;br /&gt;&lt;br /&gt;public class UserApplet extends Applet {&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Запуск апплета&lt;br /&gt;     */&lt;br /&gt;    public void start() {&lt;br /&gt;        String remoteHost = getDocumentBase().getHost();&lt;br /&gt;        int remotePort = getDocumentBase().getPort();&lt;br /&gt;        String ip = getLocalIp(remoteHost, remotePort);&lt;br /&gt;        String mac = getLocalMac();&lt;br /&gt;        sendInfo(ip, mac);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Получение локального mac&lt;br /&gt;     */&lt;br /&gt;    private String getLocalMac() {&lt;br /&gt;        try {&lt;br /&gt;            Enumeration nis = NetworkInterface.getNetworkInterfaces();&lt;br /&gt;            while(nis.hasMoreElements()) {&lt;br /&gt;                String mac = macToString((NetworkInterface) nis.nextElement());&lt;br /&gt;                if (mac != null) {&lt;br /&gt;                    return mac;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        } catch (SocketException e) {&lt;br /&gt;            showStatus("");&lt;br /&gt;        }&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private static String macToString(NetworkInterface ni)&lt;br /&gt;            throws SocketException {&lt;br /&gt;        byte mac[] = ni.getHardwareAddress();&lt;br /&gt;        if(mac != null) {&lt;br /&gt;            StringBuffer macAddress = new StringBuffer();&lt;br /&gt;            String sep = "";&lt;br /&gt;            for (int i = 0; i &lt; mac.length; i++) {&lt;br /&gt;                int b = Math.abs(mac[i]);&lt;br /&gt;                String hexByte = Integer.toHexString(b);&lt;br /&gt;                macAddress.append(sep).append(hexByte);&lt;br /&gt;                sep = ":";&lt;br /&gt;            }&lt;br /&gt;            return macAddress.toString();&lt;br /&gt;        }&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Получение локального ip&lt;br /&gt;     */&lt;br /&gt;    private String getLocalIp(String remoteHost, int remotePort) {&lt;br /&gt;        Socket socket = null;&lt;br /&gt;        try {&lt;br /&gt;            socket = new Socket(remoteHost, remotePort);&lt;br /&gt;            String localIp = socket.getLocalAddress().getHostAddress();&lt;br /&gt;            return localIp;&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            showStatus("");&lt;br /&gt;        } finally {&lt;br /&gt;            if (socket != null) {&lt;br /&gt;                socket.close();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return "unknown";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Отправляем информацию об ip и mac&lt;br /&gt;     */&lt;br /&gt;    private void sendInfo(String ip, String mac) {&lt;br /&gt;        try {&lt;br /&gt;            URL url = new URL(getDocumentBase(), "/userInfo?ip=" + ip + "&amp;mac=" + mac);&lt;br /&gt;            getAppletContext().showDocument(url, "_self");&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            showStatus("");&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;};&lt;/pre&gt;&lt;p&gt;Апплет загружает ответом нашего Spring Security фильтра &lt;code&gt;ru.someone.UserInfoFilter&lt;/code&gt; содержимым:&lt;/p&gt;&lt;pre&gt;&amp;lt;applet code=&amp;quot;UserApplet.class&amp;quot; codebase=&amp;quot;/&amp;quot; codetype=&amp;quot;application/java&amp;quot; width=&amp;quot;0&amp;quot; height=&amp;quot;0&amp;quot;&amp;gt;&amp;lt;/applet&amp;gt;&lt;/pre&gt;&lt;p&gt;После чего этот же фильтр делает необходимую проверку и посылает ответ. При логауте из системы, данные о клиенте удаляются вместе с его сессией. Чтобы это действительно было так, в &lt;code&gt;web.xml&lt;/code&gt; нужно прописать listener: &lt;code&gt;org.springframework.security.ui. session.HttpSessionEventPublisher&lt;/code&gt;. Который будет посылает эвенты об удалении сессии по всему Spring-контексту, а наш &lt;code&gt;ru.someone.UserInfoFilter&lt;/code&gt; будет их получать.&lt;/p&gt;&lt;p&gt;Исходники можно взять &lt;a href="http://www.assembla.com/spaces/nkoksharov/documents/aws61gPRSr3B9Wab7jnrAJ/download/ipmacapplet.zip"&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-4845066644512041646?l=nkoksharov.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/nkoksharov/~4/vG8k5FgsWdI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/nkoksharov/~3/vG8k5FgsWdI/ip-mac-java.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://nkoksharov.blogspot.com/2008/10/ip-mac-java.html</feedburner:origLink></item></channel></rss>
