<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-757496094257544869</atom:id><lastBuildDate>Tue, 27 Jan 2026 07:38:58 +0000</lastBuildDate><category>approach</category><category>tool</category><category>spring</category><category>maven-plugin</category><category>spring security</category><category>database</category><category>cache</category><category>hibernate</category><category>logging</category><category>tapestry</category><category>blazeds</category><category>bug</category><category>bytecode</category><category>event</category><category>liquibase</category><category>login</category><category>netty</category><category>network</category><category>performance</category><category>thought</category><category>aop</category><category>applet</category><category>benchmark</category><category>bonecp</category><category>browser</category><category>distributed</category><category>expression</category><category>flex</category><category>funny</category><category>html</category><category>http</category><category>instrumentation api</category><category>java</category><category>javascript</category><category>jvm</category><category>logout</category><category>maven</category><category>my project</category><category>nginx</category><category>project managment</category><category>redis</category><category>socketio</category><category>spring javassist</category><category>sql</category><category>tomcat</category><category>transaction</category><category>xml</category><title>Java-Bulldozer</title><description>&lt;i&gt;&quot;Nothing irks me more than opening a base class or interface and getting that ugly view of just the method names because source isn&#39;t available.&quot;&lt;/i&gt;&#xa;(c) Howard Lewis Ship</description><link>http://nkoksharov.blogspot.com/</link><managingEditor>noreply@blogger.com (Никита Кокшаров)</managingEditor><generator>Blogger</generator><openSearch:totalResults>45</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-5038335358731742593</guid><pubDate>Sat, 11 Jan 2014 14:41:00 +0000</pubDate><atom:updated>2014-01-29T21:24:17.685+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">distributed</category><category domain="http://www.blogger.com/atom/ns#">java</category><category domain="http://www.blogger.com/atom/ns#">netty</category><category domain="http://www.blogger.com/atom/ns#">redis</category><title>Redisson - распределенные и масштабируемые структуры данных Java на основе сервера Redis</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Опубликовал первый релиз проекта &lt;a href=&quot;https://github.com/mrniko/redisson&quot;&gt;Redisson&lt;/a&gt;. Проект позволяет использвать знакомые всем структуры данных Java (&lt;a href=&quot;http://http//docs.oracle.com/javase/6/docs/api/java/util/Set.html&quot;&gt;Set&lt;/a&gt;, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/List.html&quot;&gt;List&lt;/a&gt;, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/Map.html&quot;&gt;Map&lt;/a&gt;, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/Queue.html&quot;&gt;Queue&lt;/a&gt;, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/atomic/AtomicLong.html&quot;&gt;AtomicLong&lt;/a&gt;, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/Lock.html&quot;&gt;Lock&lt;/a&gt;, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/CountDownLatch.html&quot;&gt;CountDownLatch&lt;/a&gt;), а также использовть функционал publish/subscribe в распределенном виде с поддержкой масштабирования. Все это достигается благодаря использованию весьма популярного сервера &lt;a href=&quot;http://redis.io/&quot;&gt;Redis&lt;/a&gt;, для которого Redisson является лишь клиентом.&lt;br /&gt;
&lt;br /&gt;
Вам даже не придется знать команды Redis-а, да и возможности вызова команд Redis напрямую нет, т.к. цель проекта предоставить удобный api с использованием уже известных Java-интерфейсов. Для выполнения вызовов на сервере Redis используется пропаченный Redis-клиент - &lt;a href=&quot;https://github.com/wg/lettuce&quot;&gt;lettuce&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Представленный api по своей стуктуре в чем-то схож с Hazelcast api. Проект Hazelcast может хранить данные только в памяти, что не всегда удобно и влияет на надежность системы. Сервер Redis, в свою очередь, предоставляет три способа хранения данных:&lt;br /&gt;
&amp;nbsp;&amp;nbsp; 1. только в памяти&lt;br /&gt;
&amp;nbsp;&amp;nbsp; 2. переодическое сохранение на диск (snapshotting) - используется по-умолчанию&lt;br /&gt;
&amp;nbsp;&amp;nbsp; 3. лог транзакций, т.е. синхронная запись каждого изменения в лог (append-only file)&lt;br /&gt;
Таким образом, благодаря возможностям сервера Redis, мы получаем распределенные java структуры данных с возможностью хранения состояния не только в памяти.&lt;br /&gt;
&lt;br /&gt;
Доступ к хранилищу Redis реализован практически для любого языка программирования, благодаря &lt;a href=&quot;http://redis.io/clients&quot;&gt;большому количеству реализованных клиентов&lt;/a&gt;. Соотвественно данные созданные в Redisson будут доступны для любого Redis-клиента.&lt;br /&gt;
&lt;br /&gt;
Изначально проект задумывался как простая попытка реализовать Map на Java через Redis-команды. Однако дальнейшее изучение команд Redis, (в Redis есть поддержка транзакций, hashes, lists, sets) навело на мысль о реализации более сложных структур, таких как Lock и CountDownLatch.&lt;br /&gt;
&lt;br /&gt;
В проекте более 70 unit-тестов, для запуска которых (по-умолчанию они отключены) нужно запустить локально Redis-сервер и запустить maven-сборку с параметром -DskipTest=false&lt;br /&gt;
&lt;h5&gt;
Примеры использования:&lt;/h5&gt;
Распределенный Map:&lt;br /&gt;
&lt;pre class=&quot;brush: java; html-script: true&quot;&gt;        Config config = new Config();
        config.setConnectionPoolSize(10);
        config.addAddress(&quot;some.redis-server.com:8291&quot;);
        Redisson redisson = Redisson.create(config);

        ConcurrentMap&amp;lt;String, SomeObject&amp;gt; map = redisson.getMap(&quot;anyMap&quot;);
        map.put(&quot;123&quot;, new SomeObject());
        map.putIfAbsent(&quot;323&quot;, new SomeObject());
        map.remove(&quot;123&quot;);

        ...

        redisson.shutdown();

&lt;/pre&gt;
Распределенный Lock:&lt;br /&gt;
&lt;pre class=&quot;brush: java; html-script: true&quot;&gt;        Redisson redisson = Redisson.create();

        Lock lock = redisson.getLock(&quot;anyLock&quot;);
        lock.lock();
        lock.unlock();

        // или

        redisson.getLock(&quot;anyLock&quot;).lock();

        ...

        redisson.getLock(&quot;anyLock&quot;).unlock();

        ...

        redisson.shutdown();
&lt;/pre&gt;
&lt;/div&gt;
</description><link>http://nkoksharov.blogspot.com/2014/01/redisson-java-redis.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-8000919062434736890</guid><pubDate>Fri, 25 May 2012 08:12:00 +0000</pubDate><atom:updated>2012-05-25T12:46:14.416+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cache</category><category domain="http://www.blogger.com/atom/ns#">hibernate</category><category domain="http://www.blogger.com/atom/ns#">tool</category><title>Проект Hibernate Dynamic SQL Cache</title><description>&lt;p&gt;Выложил на github проект &lt;a href=&quot;https://github.com/mrniko/hibernate-dsc&quot;&gt;Hibernate Dynamic SQL Cache&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Описание&lt;/h4&gt;
&lt;p&gt;Данный проект успешно используется мной в нескольких проектах и показал хорошую эффективность.
Причиной для создания проекта послужил неэффективный механизм кэширования в Hibernate, впрочем сами разработчики открыто пишут об этом в документации.
&lt;ul&gt;&lt;li&gt;HQL-кэш, а также кэш коллекций entity-объектов будут сбрасываться при каждом обновлении данных в хотя бы в одной из таблиц, участвующих в запросе. При очень частых обновлениях таблиц польза от такого рода кэшей сводится к 0.&lt;/li&gt;&lt;li&gt;SQL-кэш держит в памяти лишь результат первого вызова запроса и не сбрасывается.&lt;/li&gt;&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;Поэтому возникла идея создания динамически обновляемого кэша. 
Результат запроса обновляется при INSERT или DELETE объекта в базе.
Для использования данного решения следует немного изменить подход к построению запросов, а также использованию различных коллекций внутри entity-объектов.
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Запрос должен возвращать только id объекта. По этому id вы можете легко загрузить объект из кэша. 
   (Таким образом получается два обращения к кэшу, что всегда быстрее выполнения запросов к базе)&lt;/li&gt;
&lt;li&gt;Запрос должен быть только по неизменяемым полям объекта.&lt;/li&gt;
&lt;li&gt;Вместо использования коллекций в entity-объекте использовать соответствующий SQL-запрос c динамическим обновлением.&lt;/li&gt;&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;Cache provider может использоваться абсолютно любой (Infinispan, Hazelcast, EHCache ...)&lt;/p&gt;

&lt;p&gt;Проект опубликован под Apache License 2.0&lt;/p&gt;


&lt;h4&gt;Пример использования&lt;/h4&gt;

&lt;p&gt;Для наглядности примера используется Spring, хотя сам проект не привязан к нему, можно использовать любой другой контейнер.&lt;/p&gt;
&lt;p&gt;Конфигурация hibernate. Здесь важно отметить использование собственного &lt;code&gt;hibernate.cache.query_cache_factory&lt;/code&gt; через который и осуществляется вся логика управления обновлениями кэшей:&lt;/p&gt;

&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;
&lt;session-factory&gt;

      ...

      &lt;property name=&quot;hibernate.cache.query_cache_factory&quot;&gt;com.corundumstudio.hibernate.dsc.DynamicQueryCacheFactory&lt;/property&gt;

      &lt;property name=&quot;hibernate.cache.region.factory_class&quot;&gt;org.hibernate.cache.infinispan.InfinispanRegionFactory&lt;/property&gt;
      &lt;property name=&quot;hibernate.cache.use_second_level_cache&quot;&gt;true&lt;/property&gt;
      &lt;property name=&quot;hibernate.cache.use_query_cache&quot;&gt;true&lt;/property&gt;

      ...

&lt;/session-factory&gt;
&lt;/script&gt;

&lt;p&gt;Регистрируем &lt;code&gt;com.corundumstudio.hibernate.dsc.QueryCacheEntityListener&lt;/code&gt; который будет отслеживать основные операции по всем entity.&lt;/p&gt;

&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java&quot;&gt;
@Configuration
public class QueryCacheListenerConfig {

    @Bean
    public QueryCacheEntityListener createCacheListener() {
            return new QueryCacheEntityListener();  
    }
    
    @PostConstruct
    protected void init() {
            EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
            registry.getEventListenerGroup(EventType.POST_UPDATE).appendListener(createCacheListener());
            registry.getEventListenerGroup(EventType.POST_INSERT).appendListener(createCacheListener());
            registry.getEventListenerGroup(EventType.POST_DELETE).appendListener(createCacheListener());
    }

}
&lt;/script&gt;

&lt;p&gt;Пример DAO-сервиса. &lt;code&gt;com.corundumstudio.hibernate.dsc.CacheCallback&lt;/code&gt; - callback через который будет обслуживать операции &quot;insert&quot; или &quot;delete&quot; на объекте (в данном примере SimpleEntity), чтобы результаты запроса были всегда &quot;свежими&quot;.
&lt;/p&gt;

&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java&quot;&gt;
@Service
public class SimpleEntityDao {

    private final String queryRegionName = &quot;SimpleEntity_Query&quot;;
    private final String query = &quot;SELECT id FROM SimpleEntity WHERE phone = :phone&quot;;

    @Autowired
    private QueryCacheEntityListener queryListener;
    @Autowired
    private SessionFactory sessionFactory;

    @PostConstruct
    protected void init() {

        // cache callback через который будет обслуживать операции
        // &quot;insert&quot; или &quot;delete&quot; на entity, чтобы результаты запроса были всегда &quot;свежими&quot;

     CacheCallback&lt;SimpleEntity&gt; handler = new CacheCallback&lt;SimpleEntity&gt;() {

      @Override
      protected void onInsertOrDelete(InsertOrDeleteCommand command,
        SimpleEntity object) {
       command.setParameter(&quot;phone&quot;, object.getPhone());
       command.setUniqueResult(object.getId());
      }

     };
     queryCacheEntityListener.register(SimpleEntity.class, queryRegionName, handler);
    }
    
    @Transactional
    public SimpleEntity getEntityByPhone(String phone) {
            Session session = sessionFactory.getCurrentSession();
            SQLQuery sqlQuery = session.createSQLQuery(query);
            sqlQuery.addScalar(&quot;id&quot;, LongType.INSTANCE);
            sqlQuery.setCacheable(true);
            sqlQuery.setCacheRegion(cacheRegionName);
            sqlQuery.setParameter(&quot;phone&quot;, phone);
            Long idResult = (Long) sqlQuery.uniqueResult();      
            return session.get(SimpleEntity.class, idResult);
    }

    ...
    
    create, delete methods...

} 
&lt;/script&gt;

&lt;p&gt;Теперь при вызове &lt;code&gt;SimpleEntityDao.getEntityByPhone&lt;/code&gt;, первый раз будет произведено обращение к БД. Последующие вызовы метода будут возвращать значение из кэша, причем если объект &lt;code&gt;SimpleEntity&lt;/code&gt; с искомым значением &lt;code&gt;phone&lt;/code&gt; будет добавлен в базу он также появится и в результате данного запроса. И наоборот, если объект с таким значением &lt;code&gt;phone&lt;/code&gt; будет удален, то результат запроса будет возвращать &lt;code&gt;null&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Если результат запроса возвращает список объектов, то в этом случае, при удалении/создании объекта, в списке будет удаляться/добавляться его &lt;code&gt;id&lt;/code&gt;&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2012/05/hibernate-dynamic-sql-cache.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7208918256239693990</guid><pubDate>Fri, 13 Apr 2012 16:42:00 +0000</pubDate><atom:updated>2012-04-13T20:49:10.183+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">bonecp</category><category domain="http://www.blogger.com/atom/ns#">tool</category><title>BoneCP - производительный пул соединений для БД</title><description>&lt;p&gt;&lt;a href=&quot;https://github.com/wwadge/bonecp&quot;&gt;BoneCP&lt;/a&gt; - библиотека управления соединениями с БД. Использую ее уже более 2-х лет в разных проектах, в том числе и в системах с нагрузкой. Автор позиционирует ее как более производительную замену библиотекам &lt;a href=&quot;http://sourceforge.net/projects/c3p0/&quot;&gt;c3p0&lt;/a&gt; и &lt;a href=&quot;http://commons.apache.org/dbcp/&quot;&gt;dbcp&lt;/a&gt;. Представленные им &lt;a href=&quot;http://jolbox.com/benchmarks.html&quot;&gt;бенчмарки&lt;/a&gt; это только подтверждают.&lt;br /&gt;&lt;p&gt;Среди полезных возможностей можно отметить:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;логирование всех sql-операций&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;доступ к статистике через JMX&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;кэширование PreparedStatement-ов&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description><link>http://nkoksharov.blogspot.com/2012/04/bonecp-connection-pool.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-3452303531262953254</guid><pubDate>Thu, 12 Jan 2012 12:16:00 +0000</pubDate><atom:updated>2012-05-07T23:06:10.945+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">netty</category><category domain="http://www.blogger.com/atom/ns#">network</category><category domain="http://www.blogger.com/atom/ns#">socketio</category><title>Поддержка socket.io протокола на Java</title><description>&lt;p&gt;Опубликовал свой первый проект на github - &lt;a href=&quot;https://github.com/mrniko/netty-socketio&quot;&gt;netty-socketio&lt;/a&gt;, реализующий серверную поддержку &lt;a href=&quot;http://socket.io&quot;&gt;socket.io&lt;/a&gt; протокола на java. В основе использован легендарный фреймворк &lt;a href=&quot;http://netty.io&quot;&gt;Netty&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Socket.io - библиотека предназначенная для создания постоянной связи браузера с сервером. Таким образом возможно организовывать доставку данных в реальном времени в обе стороны. Socket.io поддерживает несколько транспортных механизмов для организации такого взаимодействия - web-sockets, xhr-polling и т.д.&lt;/p&gt;&lt;p&gt;В моей реализации поддерживается &lt;a href=&quot;https://github.com/LearnBoost/socket.io-client&quot;&gt;socket.io-client&lt;/a&gt; версии 0.8.7+. Из поддерживаемых транспортов пока только &lt;b&gt;xhr-polling&lt;/b&gt; и &lt;b&gt;websocket&lt;/b&gt;.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2012/01/socketio-netty.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-8967670350129298476</guid><pubDate>Wed, 02 Mar 2011 15:00:00 +0000</pubDate><atom:updated>2011-03-02T19:33:14.928+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">xml</category><title>Smooks: парсим xml с помощью ... xml</title><description>&lt;p&gt;&lt;a href=&quot;http://www.smooks.org/&quot;&gt;Smooks&lt;/a&gt; - отличный инструмент, который решает задачу парсинга xml используя xml-маппинг тэгов и аттрибутов на объекты и их свойства. Вообще продукт позиционируется как инструмент для трансформации, биндинга, валидации и обработки данных в различных форматах (CSV, XML, EDI). Я рассматриваю этот продукт как достойную альтернативу других механизмов парсинга xml - JAXB, JAXP, Digester и пр.&lt;/p&gt;&lt;p&gt;Предположим нам надо распарсить такой вот xml:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;br /&gt;&lt;roles&gt;&lt;br /&gt; &lt;role id=&quot;1&quot;&gt;&lt;br /&gt;  &lt;name&gt;Босс&lt;/name&gt;&lt;br /&gt;  &lt;bonusType&gt;ENERGY_SPEED&lt;/bonusType&gt;&lt;br /&gt;  &lt;bonuses&gt;&lt;br /&gt;   &lt;bonus level=&quot;1&quot; percent=&quot;3&quot;/&gt;&lt;br /&gt;  &lt;/bonuses&gt;&lt;br /&gt; &lt;/role&gt;&lt;br /&gt; &lt;role id=&quot;2&quot;&gt;&lt;br /&gt;  &lt;name&gt;Менеджер&lt;/name&gt;&lt;br /&gt;  &lt;bonusType&gt;MAX_ENERGY&lt;/bonusType&gt;&lt;br /&gt;  &lt;bonuses&gt;&lt;br /&gt;   &lt;bonus level=&quot;1&quot; percent=&quot;10&quot;/&gt;&lt;br /&gt;   &lt;bonus level=&quot;2&quot; percent=&quot;11&quot;/&gt;&lt;br /&gt;   &lt;bonus level=&quot;3&quot; percent=&quot;12&quot;/&gt;&lt;br /&gt;  &lt;/bonuses&gt;&lt;br /&gt; &lt;/role&gt;&lt;br /&gt;&lt;/roles&gt;&lt;/script&gt;&lt;p&gt;Xml необходимо распарсить в List из объектов типа PlayerRole:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java&quot;&gt;&lt;br /&gt;public class PlayerRole {&lt;br /&gt;&lt;br /&gt;    public enum Bonus {ENERGY_SPEED, HEALTH_SPEED, MAX_ENERGY, MAX_HEALTH};&lt;br /&gt;&lt;br /&gt;    private Integer id;&lt;br /&gt;    private String name;&lt;br /&gt;    private Bonus bonusType;&lt;br /&gt;    // Map&lt;level, percent&gt;&lt;br /&gt;    private Map&lt;Integer, Double&gt; bonuses = new HashMap&lt;Integer, Double&gt;();&lt;br /&gt;&lt;br /&gt;    public String getName() {&lt;br /&gt;        return name;&lt;br /&gt;    }&lt;br /&gt;    public void setName(String name) {&lt;br /&gt;        this.name = name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Integer getCell() {&lt;br /&gt;        return cell;&lt;br /&gt;    }&lt;br /&gt;    public void setCell(Integer cell) {&lt;br /&gt;        this.cell = cell;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Bonus getBonusType() {&lt;br /&gt;        return bonusType;&lt;br /&gt;    }&lt;br /&gt;    public void setBonusType(Bonus bonusType) {&lt;br /&gt;        this.bonusType = bonusType;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Map&lt;Integer, Double&gt; getBonuses() {&lt;br /&gt;        return bonuses;&lt;br /&gt;    }&lt;br /&gt;    public void setBonuses(Map&lt;Integer, Double&gt; bonuses) {&lt;br /&gt;        this.bonuses = bonuses;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;/script&gt;&lt;p&gt;Для начала создадим xml-маппинг необходимый для преобразования данных в объекты:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;?xml version=&quot;1.0&quot;?&gt;&lt;br /&gt;&lt;smooks-resource-list&lt;br /&gt;    xmlns=&quot;http://www.milyn.org/xsd/smooks-1.1.xsd&quot;&lt;br /&gt;    xmlns:g=&quot;http://www.milyn.org/xsd/smooks/groovy-1.1.xsd&quot;&lt;br /&gt;    xmlns:jb=&quot;http://www.milyn.org/xsd/smooks/javabean-1.3.xsd&quot;&gt;&lt;br /&gt;&lt;br /&gt;    &lt;jb:bean beanId=&quot;roles-bean&quot; class=&quot;java.util.ArrayList&quot; createOnElement=&quot;roles&quot;&gt;&lt;br /&gt;        &lt;jb:wiring beanIdRef=&quot;roles-role-bean&quot; /&gt;&lt;br /&gt;    &lt;/jb:bean&gt;&lt;br /&gt;&lt;br /&gt;    &lt;jb:bean beanId=&quot;roles-role-bean&quot; class=&quot;ru.somepackage.PlayerRole&quot; createOnElement=&quot;role&quot;&gt;&lt;br /&gt;        &lt;jb:value property=&quot;id&quot; data=&quot;role @id&quot;/&gt;&lt;br /&gt;        &lt;jb:value property=&quot;name&quot; data=&quot;role/name&quot;/&gt;&lt;br /&gt;        &lt;jb:value property=&quot;bonusType&quot; data=&quot;role/bonusType&quot;/&gt;&lt;br /&gt;        &lt;jb:wiring property=&quot;bonuses&quot; beanIdRef=&quot;role-bonuses&quot;/&gt;&lt;br /&gt;    &lt;/jb:bean&gt;&lt;br /&gt;&lt;br /&gt;    &lt;jb:bean beanId=&quot;role-bonuses&quot; class=&quot;java.util.HashMap&quot; createOnElement=&quot;bonuses&quot;/&gt;&lt;br /&gt;&lt;br /&gt;    &lt;g:groovy executeOnElement=&quot;bonuses/bonus&quot;&gt;&lt;br /&gt;        &lt;g:script&gt;&lt;br /&gt;            &lt;!--&lt;br /&gt;                use(DOMCategory) {&lt;br /&gt;                    def level = element.&#39;@level&#39;;&lt;br /&gt;                    getBean(&#39;role-bonuses&#39;)[level as int] = (element.&#39;@percent&#39; as double) / 100;&lt;br /&gt;                }&lt;br /&gt;            --&gt;&lt;br /&gt;        &lt;/g:script&gt;&lt;br /&gt;    &lt;/g:groovy&gt;&lt;br /&gt;&lt;/smooks-resource-list&gt;&lt;/script&gt;&lt;p&gt;В Smooks все метаданные, необходимые для парсинга участков xml, представляются в виде бинов с сылками друг на друга. Адресация в xml осуществляется с помощью xpath-выражений. Если при парсинге необходима какая-то пост-обработка данных можно воспользоваться возможностью выполнения groovy-скриптов (тэг g:groovy), при этом код работы с xml будет более лаконичн чем на java. Из groovy также возможны обращение к бинам smooks.&lt;/p&gt;&lt;p&gt;Ну и наконец пример запуска механизма парсинга данных с приведенным конфигом:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java&quot;&gt;package ru.somepackage;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;&lt;br /&gt;import javax.xml.transform.Source;&lt;br /&gt;import javax.xml.transform.stream.StreamSource;&lt;br /&gt;&lt;br /&gt;import org.milyn.Smooks;&lt;br /&gt;import org.milyn.payload.JavaResult;&lt;br /&gt;&lt;br /&gt;public class SmooksLoader {&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        Smooks smooks = new Smooks();&lt;br /&gt;        InputStream configStream = SmooksLoader.class.getResourceAsStream(&quot;/config.xml&quot;);&lt;br /&gt;        smooks.addConfigurations(configStream);&lt;br /&gt;        JavaResult javaResult = new JavaResult();&lt;br /&gt;        try {&lt;br /&gt;            InputStream xmlStream = SmooksLoader.class.getResourceAsStream(&quot;/roles.xml&quot;);&lt;br /&gt;            Source source = new StreamSource(xmlStream);&lt;br /&gt;            smooks.filterSource(source, javaResult);&lt;br /&gt;        } catch (IOException e) {&lt;br /&gt;            throw new IllegalStateException(e);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        List&lt;PlayerRole&gt; roles = javaResult.getBean(&quot;roles-bean&quot;);&lt;br /&gt;        ....&lt;br /&gt;    }&lt;br /&gt;&lt;/script&gt;&lt;p&gt;Чтобы получить результат парсинга надо выбрать бин, обрабатывающий root-овый тэг, в нашем случае это бин с id=&quot;roles-bean&quot;.&lt;/p&gt;&lt;p&gt;Я выбраю этот движок для разбора xml т.к. он почти не требует кода и интегрирован с groovy. Использую его когда в проекте требуется распарсить различные конфигурационные ресурсы на этапе запуска сервера.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2011/03/smooks-xml-xml.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-6431785157614517659</guid><pubDate>Fri, 14 Jan 2011 15:00:00 +0000</pubDate><atom:updated>2011-01-14T18:26:23.701+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">spring javassist</category><title>Использование Javassist для генерации прокси в Spring Framework</title><description>&lt;p&gt;Известно, что проект cglib давно находится в заброшенном состоянии, также при работе с ним возникают некоторые проблемы, описанные &lt;a href=&quot;&quot;&gt;здесь&lt;/a&gt;. Наиболее популярная альтернатива этому проекту - &lt;a href=&quot;http://www.csg.is.titech.ac.jp/~chiba/javassist/&quot;&gt;javassist&lt;/a&gt;. Он используется в таких проектах как JBoss AS, Hibernate, Weld ... &lt;/p&gt;&lt;p&gt;Только вот в Spring он еще не появился. Замена библиотеки cglib, используемой для генерации proxy-объектов, на javassist, судя по плану проекта, состоится в версии 3.1 проекта. Вот &lt;a href=&quot;https://jira.springframework.org/browse/SPR-5654&quot;&gt;jira-таск&lt;/a&gt;. В этом же таске я прикрепил аттачи в виде трех классов и пары патчей, которые позволяют окончательно перейти на Javassist.&lt;/p&gt;&lt;p&gt;Для включения поддержки Javassist вам понадобится пристроить к себе в проект вот эти три класса - &lt;a href=&quot;https://jira.springframework.org/secure/attachment/17476/JavassistAopProxy.java&quot;&gt;JavassistAopProxy&lt;/a&gt;, &lt;a href=&quot;https://jira.springframework.org/secure/attachment/17477/JavassistAopProxyFactory.java&quot;&gt;JavassistAopProxyFactory&lt;/a&gt; и &lt;a href=&quot;https://jira.springframework.org/secure/attachment/17478/JavassistApplicationContext.java&quot;&gt;JavassistApplicationContext&lt;/a&gt;. Затем вместо spring-овой реализации ApplicationContext-а задействовать &lt;code&gt;org.springframework.aop.framework.JavassistApplicationContext&lt;/code&gt;. Если вы не создаете его напрямую, а используете &lt;code&gt;org.springframework.web.context.ContextLoaderListener&lt;/code&gt; сконфигурированный в web.xml, то вам необоходимо будет добавить в этот же web.xml такие строки:&lt;br /&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml;&quot;&gt;&lt;br /&gt;&lt;context-param&gt;&lt;br /&gt;   &lt;param-name&gt;contextClass&lt;/param-name&gt;&lt;br /&gt;   &lt;param-value&gt;org.springframework.aop.framework.JavassistApplicationContext&lt;/param-value&gt;&lt;br /&gt;&lt;/context-param&gt;&lt;/script&gt;&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2011/01/javassist-spring-framework.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7153334418694153168</guid><pubDate>Sat, 06 Nov 2010 19:30:00 +0000</pubDate><atom:updated>2010-11-06T22:36:50.429+03:00</atom:updated><title>&quot;Хорошая&quot; новость от Oracle...</title><description>&lt;p&gt;Хотя это событие было предсказуемо, после коммерциализации MySQL и OpenOffice, верить в него не хотелось. Похоже oracle хочет разделить версии JVM на &quot;free&quot; и &quot;premium&quot;, подробнее &lt;a href=&quot;http://www.theregister.co.uk/2010/11/06/oracle_dueling_jvms/&quot;&gt;тут&lt;/a&gt;.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2010/11/oracle.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-2560347677040750337</guid><pubDate>Wed, 03 Nov 2010 15:04:00 +0000</pubDate><atom:updated>2010-11-03T19:07:34.557+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><title>Поиск по ключу входящему в числовой диапазон.</title><description>&lt;p&gt;Дана таблица по которой, в зависимости от опыта игрока, определяется его уровень:&lt;/p&gt;&lt;pre&gt;  Диапазон опыта     Уровень&lt;br /&gt;&lt;br /&gt;   0 &lt; exp &lt;= 12        1&lt;br /&gt;  12 &lt; exp &lt;= 30        2&lt;br /&gt;  30 &lt; exp &lt;= 67        3&lt;br /&gt;       ...             ...&lt;br /&gt;  231 &lt; exp &lt;= 300      10&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Задача: реализовать алгоритм поиска уровня.&lt;/p&gt;&lt;p&gt;Для решения задачи я задействую класс &lt;code&gt;java.util.TreeMap&lt;/code&gt;. Почему этот класс? Все просто, он реализует важный интерфейс - &lt;code&gt;java.util.NavigableMap&lt;/code&gt;, появившийся с версии Java 1.6. Его метод&lt;/p&gt;&lt;code&gt;Map.Entry&amp;lt;K,V&amp;gt; ceilingEntry(K key)&lt;/code&gt;&lt;p&gt;как раз позволяет найти запись в map-е удовлетворяющую условию K(n) &lt; key &lt;= K(n+1) и при этом будет содержать значение по ключу K(n+1). Таким образом, остается составить map-у из записей: ключ - макс. значение опыта на уровне, значение - уровень. Код решения:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java;&quot;&gt;&lt;br /&gt;...&lt;br /&gt;private final NavigableMap&lt;Integer, Integer&gt; lMap = new TreeMap&lt;Integer, Integer&gt;();&lt;br /&gt;&lt;br /&gt;public void init() {&lt;br /&gt; lMap.put(12, 1);&lt;br /&gt; lMap.put(30, 2);&lt;br /&gt; lMap.put(67, 3);&lt;br /&gt; ...&lt;br /&gt; lMap.put(300, 10);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public Integer findLevel(Integer exp) {&lt;br /&gt;    return lMap.ceilingEntry(exp).getValue();&lt;br /&gt;}&lt;br /&gt;...&lt;/script&gt;&lt;p&gt;Для наглядности я захардкодил map-у в коде, на деле же она создается из xml.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2010/11/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-8911987378485208755</guid><pubDate>Fri, 29 Oct 2010 18:57:00 +0000</pubDate><atom:updated>2010-11-03T23:38:46.176+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">performance</category><title>Обновление максимального значения без использования блокировок</title><description>&lt;p&gt;При реализации грамотных многопоточных механизмов, порой используют только синхронизованные блоки кода. Но, не следует также забывать и о функции CAS (Compare and swap, представленной соответствующей процессорной командой). Данная функция присутствует во всех классах пакета &lt;code&gt;java.util.concurrent.atomic&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Алгортимы с использованием CAS являются безблокировочными, ведь потокам не приходится ожидать блокировок для их исполнения. Операция CAS выполняется успешно или неуспешно, но независимо от исхода она завершается в предсказуемые сроки. В случае неуспешности выполнения программа может сделать попытку выполнения этой функции, либо выполнить другие операции. &lt;/p&gt;&lt;p&gt;До недавнего времени примение CAS-у в своей практике я не находил. Однако, когда потребовалось реализовать многопоточный алгоритм обновления максимального значения, он оказался весьма уместен:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java;&quot;&gt;&lt;br /&gt;final AtomicLong max = new AtomicLong(Long.MIN_VALUE);&lt;br /&gt;...&lt;br /&gt;public void updateMax(long val) {&lt;br /&gt;    while(true) {&lt;br /&gt;        long current = max.get();&lt;br /&gt;        if (val &lt; current&lt;br /&gt;              || max.compareAndSet(current, val)) {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;...&lt;/script&gt;</description><link>http://nkoksharov.blogspot.com/2010/10/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-881908433904661158</guid><pubDate>Sun, 08 Aug 2010 08:49:00 +0000</pubDate><atom:updated>2010-08-08T13:25:50.510+04:00</atom:updated><title>Реализация concurrent HashSet-а в Java</title><description>&lt;p&gt;К сожалению, в JDK нет полноценной реализации concurrent HashSet-а, как в случае для HashMap - &lt;code&gt;java.util.concurrent.ConcurrentHashMap&lt;/code&gt;. Есть &lt;code&gt;CopyOnWriteArraySet&lt;/code&gt;, использующий внутри себя &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;, но у него есть некоторые недостатки:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;поиск элементов будет медленне ибо в List-ах он выполняется по equals, а не по hashcode -&gt; equals.&lt;/li&gt;&lt;li&gt;невозможность удаления объектов при итерации (iterator.remove() - не реализован) и понятно почему, ведь при итерации используется копия коллекции.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Но выход есть, подобие ConcurrentHashSet можно сделать на основе ConcurrentHashMap, записывая как ключ элемент, а в значение какой-нибудь dummy-объект. Такая реализация представлена в виде &lt;code&gt;org.jboss.util.collection.ConcurrentSet&lt;/code&gt; из бибилиотеки &lt;a href=&quot;http://community.jboss.org/wiki/JBossCommonProject&quot;&gt;jboss-common-core&lt;/a&gt;.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2010/08/concurrent-hashset-java.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-5079082816062964300</guid><pubDate>Tue, 06 Apr 2010 13:32:00 +0000</pubDate><atom:updated>2010-04-08T13:06:56.477+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">spring</category><category domain="http://www.blogger.com/atom/ns#">transaction</category><title>Транзакционные синхронизации в Spring Framework</title><description>&lt;p&gt;Выполнение определенной логики по окончанию текущей транзакции в &lt;a href=&quot;http://www.springsource.org/&quot;&gt;Spring Framework&lt;/a&gt; можно &quot;подключать&quot; динамически с помощью так называемых транзакционных синхронизаций.&lt;/p&gt;&lt;p&gt;Транзакционная синхронизация - это по сути callback-объект типа &lt;code&gt;org.springframework.transaction.support.TransactionSynchronization&lt;/code&gt;, который содержит в себе определенные методы вызываемые из AbstractPlatformTransactionManager на соответствующие действия над транзакцией (afterCompletion, afterCommit, afterRollback, suspend и т.д.). &lt;code&gt;org.springframework.transaction.support.TransactionSynchronizationManager&lt;/code&gt; - менеджер со статическими методами, используемый для регистрации и управления синхронизациями. При написании своей синхронизации в качестве базового класса удобно использовать &lt;code&gt;org.springframework.transaction.support.TransactionSynchronizationAdapter&lt;/code&gt;. Вот пример:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java;&quot;&gt;&lt;br /&gt;...&lt;br /&gt;@Transactional&lt;br /&gt;public void someMethod() {&lt;br /&gt;      if (TransactionSynchronizationManager.isActualTransactionActive()) {&lt;br /&gt;          TransactionSynchronizationManager.registerSynchronization(&lt;br /&gt;                                  new TransactionSynchronizationAdapter() {&lt;br /&gt;              @Override&lt;br /&gt;              public void afterCompletion(int status) {&lt;br /&gt;                  // вызов этого куска кода произойдет &lt;br /&gt;                  // в результате любого окончания транзакции &lt;br /&gt;                  // rollback-а или commit-а&lt;br /&gt;              }&lt;br /&gt;          });&lt;br /&gt;      }&lt;br /&gt;      ...&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;&lt;/script&gt;</description><link>http://nkoksharov.blogspot.com/2010/04/spring-framework.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-1512009880681305313</guid><pubDate>Fri, 05 Mar 2010 23:55:00 +0000</pubDate><atom:updated>2010-03-06T21:06:51.391+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">blazeds</category><title>Сериализация полей без set-методов и поддержка аннотаций в BlazeDS</title><description>&lt;p&gt;Для передачи данных между &lt;a href=&quot;http://opensource.adobe.com/wiki/display/blazeds/BlazeDS/&quot;&gt;BlazeDS 3.2&lt;/a&gt; и клиентом рекомендуется использовать value object-ы. Эти объекты содержат только те данные, которые действительно необходимы клиенту.&lt;/p&gt;&lt;p&gt;К сожалению, в BlazeDS есть некоторые неудобства при сериализации объектов:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Обязательное наличие set-метода (можно и пустого), если вы хотите сериализовать поле у которого есть только get-метод&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Сериализация enum-ов как объектов, вместо использования строчного значения, как заявлено в документации&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Так же не очень удобно, на мой взгляд, является создание отдельных классов для каждого value object-a. Было бы удобнее помечать аннтоциями getter-ы или поля, которые будут учавствовать/отсутсвовать в value object-е, без создания нового класса.&lt;/p&gt;&lt;p&gt;Чтобы устранить вышеперечисленные недостатки придется разобраться с механизмом сериализации BlazeDS. Кратко опишу классы, которые задействованы при сериализации: &lt;code&gt;flex.messaging.io.PropertyProxy&lt;/code&gt; - основной интерфейс, используемый для сериализации/десериализации объектов. Каждому классу объекта, передаваемому на клиента, соответствует определенная реализация &lt;code&gt;PropertyProxy&lt;/code&gt;, в основном это &lt;code&gt;flex.messaging.io.BeanProxy&lt;/code&gt;. Соответствие (Класс объекта - PropertyProxy-объект) держится в классе &lt;code&gt;flex.messaging.io.PropertyProxyRegistry&lt;/code&gt;, при сериализации/десериализации BlazeDS получает &lt;code&gt;PropertyProxy&lt;/code&gt; для какого-либо объекта используя методы: &lt;code&gt;getProxy&lt;/code&gt; и &lt;code&gt;getProxyAndRegister&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Т.к. &lt;code&gt;PropertyProxyRegistry&lt;/code&gt; является singleton-ом, то замену реализации нам придется сделать через смену значения поля &lt;code&gt;registry&lt;/code&gt;, которое спрятано внутри класса. Реализация, в которой убраны перечисленные выше недостатки, выглядит так:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java;&quot;&gt;package com.blogspot.nkoksharov.blazeds;&lt;br /&gt;&lt;br /&gt;import java.beans.*;&lt;br /&gt;import java.lang.reflect.Method;&lt;br /&gt;import java.util.*;&lt;br /&gt;&lt;br /&gt;import javax.sql.RowSet;&lt;br /&gt;&lt;br /&gt;import org.springframework.flex.remoting.RemotingExclude;&lt;br /&gt;import org.springframework.beans.FatalBeanException;&lt;br /&gt;&lt;br /&gt;import java.lang.reflect.Field;&lt;br /&gt;&lt;br /&gt;import flex.messaging.MessageException;&lt;br /&gt;import flex.messaging.io.*;&lt;br /&gt;&lt;br /&gt;public class ExPropertyProxyRegistry extends PropertyProxyRegistry {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;    * Подмена реализации&lt;br /&gt;    */&lt;br /&gt;    static {&lt;br /&gt;        try {&lt;br /&gt;            Field field = PropertyProxyRegistry.class.getDeclaredField(&quot;registry&quot;);&lt;br /&gt;            field.setAccessible(true);&lt;br /&gt;            field.set(null, new ExPropertyProxyRegistry());&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            throw new FatalBeanException(&quot;Can&#39;t replace registry implementation&quot;, e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @SuppressWarnings(&quot;unchecked&quot;)&lt;br /&gt;    private PropertyProxy guessProxy(PropertyProxy proxy, Class clazz) {&lt;br /&gt;        if (proxy != null) {&lt;br /&gt;            return proxy;&lt;br /&gt;        }&lt;br /&gt;        PropertyProxy newProxy;&lt;br /&gt;&lt;br /&gt;        if (Map.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new MapProxy();&lt;br /&gt;        } else if (Throwable.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new ThrowableProxy();&lt;br /&gt;        } else if (PageableRowSet.class.isAssignableFrom(clazz) || RowSet.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new PageableRowSetProxy();&lt;br /&gt;        } else if (Dictionary.class.isAssignableFrom(clazz)) {&lt;br /&gt;            newProxy = new DictionaryProxy();&lt;br /&gt;        } else if (clazz.isEnum()) { // фикс сериализации enum-ов&lt;br /&gt;            return null;&lt;br /&gt;        } else {&lt;br /&gt;            newProxy = new BeanProxy();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        SerializationDescriptor descriptor = createDescriptor(clazz);&lt;br /&gt;        newProxy.setDescriptor(descriptor);&lt;br /&gt;        newProxy.setIncludeReadOnly(true); // фикс сериализации get-методов&lt;br /&gt;        return newProxy;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;    * Создаем дескриптор сериализации, который исключает из сериализации значения  &lt;br /&gt;    * get-методов аннтированных org.springframework.flex.remoting.RemotingExclude&lt;br /&gt;    */&lt;br /&gt;    private SerializationDescriptor createDescriptor(Class&lt;?&gt; clazz) {&lt;br /&gt;        List&lt;String&gt; excludeProps = new ArrayList&lt;String&gt;();&lt;br /&gt;        try {&lt;br /&gt;            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);&lt;br /&gt;            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();&lt;br /&gt;            for (PropertyDescriptor pd : pds) {&lt;br /&gt;                Method method = pd.getReadMethod();&lt;br /&gt;                if (method != null &amp;&amp; method.isAnnotationPresent(RemotingExclude.class)) {&lt;br /&gt;                    excludeProps.add(pd.getName());&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        } catch (IntrospectionException e) {&lt;br /&gt;            throw new MessageException(e);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        SerializationDescriptor descriptor = new SerializationDescriptor();&lt;br /&gt;        descriptor.setExcludes(excludeProps);&lt;br /&gt;        return descriptor;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    @SuppressWarnings(&quot;unchecked&quot;)&lt;br /&gt;    public PropertyProxy getProxy(Class clazz) {&lt;br /&gt;        PropertyProxy proxy = super.getProxy(clazz);&lt;br /&gt;        return guessProxy(proxy, clazz);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    @SuppressWarnings(&quot;unchecked&quot;)&lt;br /&gt;    public PropertyProxy getProxyAndRegister(Class clazz) {&lt;br /&gt;        PropertyProxy proxy = super.getProxyAndRegister(clazz);&lt;br /&gt;        return guessProxy(proxy, clazz);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/script&gt;</description><link>http://nkoksharov.blogspot.com/2009/03/set-blazeds.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7250847433944451715</guid><pubDate>Tue, 29 Dec 2009 17:39:00 +0000</pubDate><atom:updated>2009-12-29T22:09:10.737+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">my project</category><title>Запуск &quot;Наемники : Карты судьбы&quot;</title><description>&lt;p&gt;Не так давно наступил день, который я так долго ждал. 25 декабря был запущен проект для ВКонтакте - игра &lt;b&gt;&quot;Наемники : Карты судьбы&quot;&lt;/b&gt;, над которым я трудился последние 2,5 месяца. Серверная часть проекта была реализована полностью мной, что называется &quot;своими руками&quot;. Давненько мечтал о таком маленьком проекте, в котором я смогу применить все свои знания и опыт в области разработки.&lt;/p&gt;&lt;p&gt;Использовались следующие java-технологии:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Spring&lt;/li&gt;&lt;li&gt;Spring security&lt;/li&gt;&lt;li&gt;Spring BlazeDS Integration&lt;/li&gt;&lt;li&gt;BlazeDS&lt;/li&gt;&lt;li&gt;Hibernate&lt;/li&gt;&lt;li&gt;JBoss Cache&lt;/li&gt;&lt;li&gt;Unitils&lt;/li&gt;&lt;li&gt;Jmockit&lt;/li&gt;&lt;li&gt;DBUnit&lt;/li&gt;&lt;li&gt;TestNG&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Думаю, что некоторые из вас скажут, что с использованием Hibernate хорошей производительности не добьешься, но я с этим не соглашусь. Да, в процессе разработки в Hibernate нашлись некоторые баги и выявилось весьма &quot;необычное&quot; поведение, но все эти проблемы можно решить.&lt;/p&gt;&lt;p&gt;Теперь, когда проект запущен, я могу снова начинать писать статьи, освещая некоторые проблемы с которыми я столкнулся при разработке.&lt;/p&gt;&lt;p&gt;Отмечу также и тот факт, что более 80% кода было покрыто тестами. Хотя этот процесс и занимал у меня до 50% времени, я считаю, что именно благодаря тестам удалось выйти в релиз с минимальным кол-вом багов на серверной стороне. Без тестов был бы невозможен безболезненный рефакторинг и быстрое добавление новой функциональности.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2009/12/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>13</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-6634198279393130409</guid><pubDate>Wed, 28 Oct 2009 12:24:00 +0000</pubDate><atom:updated>2010-01-12T17:09:10.959+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">spring</category><title>Динамическая модификация контекста в Spring</title><description>&lt;p&gt;Существует механизм изменения контекста Spring, который запускается после сбора всей информации о нем (из xml и аннотаций) и срабатывает до создания самих bean-ов. Я говорю об интерфейсе &lt;code&gt;org.springframework.beans.factory.config.BeanFactoryPostProcessor&lt;/code&gt;, реализуя который вы получаете именно такую возможность. Используя данный механизм очень удобно менять параметры уже объявленных где-то в контексте bean-ов. Объект реализующий этот интерфейс можно зарегистрировать как обычный bean в контексте, либо добавить его на этапе создания контекста, например так:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: java&quot;&gt;&lt;![CDATA[&lt;br /&gt;package com.blogspot.nkoksharov;&lt;br /&gt;&lt;br /&gt;import org.springframework.beans.*;&lt;br /&gt;import org.springframework.beans.factory.config.*;&lt;br /&gt;&lt;br /&gt;public class SpringTest {&lt;br /&gt;&lt;br /&gt;    public void testPostProcessor() {&lt;br /&gt;        ConfigurableApplicationContext context = &lt;br /&gt;             new ClassPathXmlApplicationContext(new String[] {&quot;/spring/context.xml&quot;}, 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(&quot;sampleBean&quot;);&lt;br /&gt;                MutablePropertyValues values = definition.getPropertyValues();&lt;br /&gt;                MockObject object = new MockObject();&lt;br /&gt;                values.addPropertyValue(&quot;someProperty&quot;, object);&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;        context.refresh();&lt;br /&gt;        &lt;br /&gt;        ... &lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}]]&gt;&lt;/script&gt;&lt;p&gt;Только не забудьте указать в конструкторе контекста аргументу &lt;code&gt;refresh&lt;/code&gt; значение &lt;code&gt;false&lt;/code&gt;, иначе наш механизм будет вызываться лишь для новых bean-ов добавляемых уж после создания контекста (как это делать я писал &lt;a href=&quot;http://nkoksharov.blogspot.com/2009/02/bean-spring.html&quot;&gt;тут&lt;/a&gt;). При вызове метода &lt;code&gt;refresh&lt;/code&gt; произойдет инициализация контекста и наш обработчик будет вызван.&lt;/p&gt;&lt;p&gt;Описанный выше прием оказывается очень полезен в использовании при тестировании, когда нужно изменить какие-то из параметров bean-ов или просто выставить свои значения.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2009/10/spring.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></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#">approach</category><category domain="http://www.blogger.com/atom/ns#">maven</category><category domain="http://www.blogger.com/atom/ns#">maven-plugin</category><title>Управление версиями в Maven</title><description>&lt;p&gt;После очередного релиза проекта приходится менять его версию, проходясь при этом по всем pom-файлам. Процедура эта довольно утомительная и избавить от нее поможет &lt;a href=&quot;http://mojo.codehaus.org/versions-maven-plugin/&quot;&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;</description><link>http://nkoksharov.blogspot.com/2009/08/maven.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>5</thr:total></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#">nginx</category><category domain="http://www.blogger.com/atom/ns#">performance</category><category domain="http://www.blogger.com/atom/ns#">tapestry</category><title>Параметры производительности nginx и tapestry 5</title><description>&lt;p&gt;Думаю, что вам известно о HTTP-сервере &lt;a href=&quot;http://sysoev.ru/nginx/&quot;&gt;nginx&lt;/a&gt;, а также возможности его использования в качестве front-end сервера для передачи разных ресурсов клиенту. Использование nginx позволяет разгрузить ваш сервер Java-приложений от лишней работы со стататическими ресурсами.&lt;/p&gt;&lt;p&gt;Отмечу лишь несколько основных, на мой взгляд параметров, влияющих на производительность системы использующей Nginx и веб-фреймворка &lt;a href=&quot;http://tapestry.apache.org&quot;&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;</description><link>http://nkoksharov.blogspot.com/2009/08/nginx-tapestry-5.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-1308552199719325887</guid><pubDate>Mon, 03 Aug 2009 15:30:00 +0000</pubDate><atom:updated>2010-01-12T17:22:13.629+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">database</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#">maven-plugin</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=&quot;http://mojo.codehaus.org/maven-hibernate3/hibernate3-maven-plugin/&quot;&gt;hibernate3-maven-plugin&lt;/a&gt; к модулю, в котором используется hibernate:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;properties&gt;&lt;br /&gt;      &lt;hibernate.update&gt;false&lt;/hibernate.update&gt;&lt;br /&gt;      &lt;hibernate.create&gt;false&lt;/hibernate.create&gt;&lt;br /&gt;&lt;/properties&gt;&lt;br /&gt;&lt;build&gt;&lt;br /&gt;      &lt;plugins&gt;&lt;br /&gt;          ...&lt;br /&gt;          &lt;plugin&gt;&lt;br /&gt;            &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;&lt;br /&gt;            &lt;artifactId&gt;hibernate3-maven-plugin&lt;/artifactId&gt;&lt;br /&gt;            &lt;version&gt;2.2&lt;/version&gt;&lt;br /&gt;            &lt;configuration&gt;&lt;br /&gt;              &lt;componentProperties&gt;&lt;br /&gt;                &lt;outputfilename&gt;schema.sql&lt;/outputfilename&gt;&lt;br /&gt;                &lt;configurationfile&gt;/resources/hibernate.cfg.xml&lt;/configurationfile&gt;&lt;br /&gt;                &lt;export&gt;false&lt;/export&gt;&lt;br /&gt;                &lt;update&gt;${hibernate.update}&lt;/update&gt;&lt;br /&gt;                &lt;create&gt;${hibernate.create}&lt;/create&gt;&lt;br /&gt;                &lt;drop&gt;true&lt;/drop&gt;&lt;br /&gt;              &lt;/componentProperties&gt;&lt;br /&gt;            &lt;/configuration&gt;&lt;br /&gt;            &lt;dependencies&gt;&lt;br /&gt;                &lt;dependency&gt;&lt;br /&gt;                    &lt;groupId&gt;postgresql&lt;/groupId&gt;&lt;br /&gt;                    &lt;artifactId&gt;postgresql&lt;/artifactId&gt;&lt;br /&gt;                    &lt;version&gt;8.3-603.jdbc4&lt;/version&gt;&lt;br /&gt;                &lt;/dependency&gt;&lt;br /&gt;            &lt;/dependencies&gt;&lt;br /&gt;          &lt;/plugin&gt;&lt;br /&gt;      &lt;/plugins&gt;&lt;br /&gt;&lt;/build&gt;&lt;/script&gt;&lt;p&gt;Необоходимым условием для работы плагина является наличие файла &lt;code&gt;hibernate.hbm.xml&lt;/code&gt;, в нем должны содержаться следующие настройки:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;hibernate-configuration&gt;&lt;br /&gt;    &lt;session-factory&gt;&lt;br /&gt;        &lt;property name=&quot;hibernate.connection.driver_class&quot;&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name=&quot;hibernate.connection.url&quot;&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name=&quot;hibernate.connection.username&quot;&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name=&quot;hibernate.connection.password&quot;&gt;...&lt;/property&gt;&lt;br /&gt;        &lt;property name=&quot;hibernate.dialect&quot;&gt;...&lt;/property&gt;&lt;br /&gt;&lt;br /&gt;        &lt;mapping class=&quot;ru.somesite.SomeObject&quot;/&gt;&lt;br /&gt;        ...&lt;br /&gt;    &lt;/session-factory&gt;&lt;br /&gt;&lt;/hibernate-configuration&gt;&lt;/script&gt;&lt;p&gt;Не забудьте также и о том, что теперь все маппинги классов тоже нужно указывать в этом файле, а не в каком-нибудь SessionFactoryBean, если используете Spring. Настройка SessionFactoryBean будет выглядеть примерно так:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;bean id=&quot;sessionFactory&quot;&lt;br /&gt;        class=&quot;org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean&quot;&gt;&lt;br /&gt;        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;&lt;br /&gt;        &lt;property name=&quot;configLocation&quot; value=&quot;classpath:/hibernate.cfg.xml&quot; /&gt;&lt;br /&gt;        &lt;property name=&quot;hibernateProperties&quot;&gt;&lt;br /&gt;            &lt;props&gt;&lt;br /&gt;                ...&lt;br /&gt;                &lt;prop key=&quot;hibernate.hbm2ddl.auto&quot;&gt;validate&lt;/prop&gt;&lt;br /&gt;            &lt;/props&gt;&lt;br /&gt;        &lt;/property&gt;&lt;br /&gt;&lt;/bean&gt;&lt;/script&gt;&lt;p&gt;Стоит отметить, что параметру &lt;code&gt;hibernate.hbm2ddl.auto&lt;/code&gt;, в вашем проекте, нужно присвоить значение &lt;code&gt;validate&lt;/code&gt;. Теперь при каждом выполнении команды:&lt;/p&gt;&lt;pre&gt;mvn hibernate3:hbm2ddl -Dhibernate.create=true&lt;/pre&gt;&lt;p&gt;вам будет выводится sql генерируемый hibernate, который можно вставить в changeset. Параметр &lt;code&gt;-Dhibernate.create=true&lt;/code&gt; следует указывать, в том случае, когда вам требуется SQL всей схемы БД. В случае, когда требуется получить SQL-команды, выполняемые hibernate для обновления схемы БД, то вместо первого параметра используйте &lt;code&gt;-Dhibernate.update=true&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;И помните, что в случае обновления БД вам нужно будет следить SQL, генерируемым hibernate, т.к. не все изменения параметров объектов входят в SQL (к примеру, hibernate не будет создавать некоторые constraint-ы).&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2009/07/liquibase-hibernate-maven-2.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-757496094257544869.post-7105586250414106802</guid><pubDate>Thu, 09 Jul 2009 18:00:00 +0000</pubDate><atom:updated>2010-01-12T17:31:17.825+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">approach</category><category domain="http://www.blogger.com/atom/ns#">database</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#">maven-plugin</category><title>Использование LiquiBase с Hibernate и Maven. Часть 1</title><description>&lt;p&gt;Когда-то я представлял вашему вниманию инструмент для управления изменениями в БД - &lt;a href=&quot;http://liquibase.org/&quot;&gt;liquibase&lt;/a&gt;. Теперь я решил описать как его можно использовать в проектах с &lt;a href=&quot;http://maven.apache.org/&quot;&gt;maven&lt;/a&gt;-сборкой, и &lt;a href=&quot;http://www.hibernate.org/&quot;&gt;hibernate&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Для начала необходимо создать отдельный maven-артефакт, который будет содержать только ресурсы - xml-файлы с changeSet-ами от liquibase. Структура файлов в папке &lt;code&gt;resources&lt;/code&gt; нашего артефакта следующая:&lt;/p&gt;&lt;pre&gt;    /changelogs/версия_проекта/*-changelog.xml&lt;br /&gt;    /changelogs/версия_проекта/release-changelog.xml&lt;br /&gt;    /project-changelogs.xml&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Файл &lt;code&gt;project-changesets.xml&lt;/code&gt; включает в себя набор файлов &lt;code&gt;release-changelog.xml&lt;/code&gt; всех версий проекта, вот пример его содержимого:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;databaseChangeLog&lt;br /&gt;        xmlns=&quot;http://www.liquibase.org/xml/ns/dbchangelog/1.9&quot;&lt;br /&gt;        xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&lt;br /&gt;        xsi:schemaLocation=&quot;http://www.liquibase.org/xml/ns/dbchangelog/1.9 &lt;br /&gt;                http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd&quot;&gt;&lt;br /&gt;&lt;br /&gt;     &lt;include file=&quot;changelogs/1.0/release-changelog.xml&quot; relativeToChangelogFile=&quot;true&quot;/&gt;&lt;br /&gt;     &lt;include file=&quot;changelogs/1.1/release-changelog.xml&quot; relativeToChangelogFile=&quot;true&quot;/&gt;&lt;br /&gt;     &lt;include file=&quot;changelogs/2.0/release-changelog.xml&quot; relativeToChangelogFile=&quot;true&quot;/&gt;&lt;br /&gt;     ...&lt;br /&gt;&lt;/databaseChangeLog&gt;&lt;/script&gt;&lt;p&gt;Таким же образом файлы &lt;code&gt;release-changelog.xml&lt;/code&gt; включают в себя список файлов с измененями, находящихся в той же папке и касающихся лишь данной версии проекта.  Сами изменения я предпочитаю описывать не пользуясь при это тэгами liquibase, а использую привычный SQL.&lt;/p&gt;&lt;p&gt;Благодаря наличию в liquibase такого понятия как &quot;контекст&quot;, можно разделить все операции над БД на три контекста:&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; - контекст используемый при изменении &quot;живых&quot; данных, т.е данных появляющихся в системе при ее эксплуатации.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Такая концепция позволит вам четко разделять виды изменений в БД. А также &quot;накатывать&quot; изменения лишь нужных контекстов. Пример:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;changeSet id=&quot;2&quot; author=&quot;nkoksharov&quot; context=&quot;initdata&quot;&gt;&lt;br /&gt;        &lt;sql&gt;&lt;br /&gt;           insert into roles values (123, &#39;ROLE_USER&#39;);&lt;br /&gt;        &lt;/sql&gt;&lt;br /&gt;        &lt;rollback&gt;&lt;br /&gt;            &lt;sql&gt;&lt;br /&gt;               delete from roles where id = 123;&lt;br /&gt;            &lt;/sql&gt;&lt;br /&gt;        &lt;/rollback&gt;&lt;br /&gt;&lt;/changeSet&gt;&lt;/script&gt;&lt;p&gt;Если в проекте предполагается использовать две базы - БД разработчика/продакшн и БД для тестов, то pom-файл, созданного нами артефакта, будет содержать следующие строки:&lt;/p&gt;&lt;script type=&quot;syntaxhighlighter&quot; class=&quot;brush: xml&quot;&gt;&lt;project&gt;&lt;br /&gt;    &lt;properties&gt;&lt;br /&gt;        &lt;liquibase.goal&gt;update&lt;/liquibase.goal&gt;&lt;br /&gt;    &lt;/properties&gt;&lt;br /&gt;    &lt;profiles&gt;&lt;br /&gt;        &lt;profile&gt;&lt;br /&gt;            &lt;id&gt;devdb&lt;/id&gt;&lt;br /&gt;            &lt;activation&gt;&lt;br /&gt;                &lt;activeByDefault&gt;true&lt;/activeByDefault&gt;&lt;br /&gt;            &lt;/activation&gt;&lt;br /&gt;            &lt;properties&gt;&lt;br /&gt;                &lt;database.url&gt;${app.database.url}&lt;/database.url&gt;&lt;br /&gt;                &lt;database.user&gt;${app.database.user}&lt;/database.user&gt;&lt;br /&gt;                &lt;database.password&gt;${app.database.password}&lt;br /&gt;                &lt;/database.password&gt;&lt;br /&gt;                &lt;database.contexts&gt;${app.database.contexts}&lt;br /&gt;                &lt;/database.contexts&gt;&lt;br /&gt;            &lt;/properties&gt;&lt;br /&gt;        &lt;/profile&gt;&lt;br /&gt;        &lt;profile&gt;&lt;br /&gt;            &lt;id&gt;testdb&lt;/id&gt;&lt;br /&gt;            &lt;properties&gt;&lt;br /&gt;                &lt;database.url&gt;${test.database.url}&lt;/database.url&gt;&lt;br /&gt;                &lt;database.user&gt;${test.database.user}&lt;/database.user&gt;&lt;br /&gt;                &lt;database.password&gt;${test.database.password}&lt;br /&gt;                &lt;/database.password&gt;&lt;br /&gt;                &lt;database.contexts&gt;${test.database.contexts}&lt;br /&gt;                &lt;/database.contexts&gt;&lt;br /&gt;            &lt;/properties&gt;&lt;br /&gt;        &lt;/profile&gt;&lt;br /&gt;    &lt;/profiles&gt;&lt;br /&gt;&lt;br /&gt;    &lt;build&gt;&lt;br /&gt;        &lt;plugins&gt;&lt;br /&gt;            &lt;plugin&gt;&lt;br /&gt;                &lt;groupId&gt;org.liquibase&lt;/groupId&gt;&lt;br /&gt;                &lt;artifactId&gt;liquibase-plugin&lt;/artifactId&gt;&lt;br /&gt;                &lt;version&gt;1.9.3.0&lt;/version&gt;&lt;br /&gt;                &lt;configuration&gt;&lt;br /&gt;                    &lt;changeLogFile&gt;src/main/resources/project-changelogs.xml&lt;br /&gt;                    &lt;/changeLogFile&gt;&lt;br /&gt;                    &lt;driver&gt;org.postgresql.Driver&lt;/driver&gt;&lt;br /&gt;&lt;br /&gt;                    &lt;url&gt;${database.url}&lt;/url&gt;&lt;br /&gt;                    &lt;username&gt;${database.user}&lt;/username&gt;&lt;br /&gt;                    &lt;password&gt;${database.password}&lt;/password&gt;&lt;br /&gt;                    &lt;contexts&gt;${database.contexts}&lt;/contexts&gt;&lt;br /&gt;                &lt;/configuration&gt;&lt;br /&gt;                &lt;executions&gt;&lt;br /&gt;                    &lt;execution&gt;&lt;br /&gt;                        &lt;id&gt;appdb&lt;/id&gt;&lt;br /&gt;                        &lt;phase&gt;process-resources&lt;/phase&gt;&lt;br /&gt;                        &lt;configuration&gt;&lt;br /&gt;                            &lt;contexts&gt;${app.database.contexts}&lt;br /&gt;                            &lt;/contexts&gt;&lt;br /&gt;                            &lt;url&gt;${app.database.url}&lt;/url&gt;&lt;br /&gt;                            &lt;username&gt;${app.database.user}&lt;/username&gt;&lt;br /&gt;                            &lt;password&gt;${app.database.password}&lt;br /&gt;                            &lt;/password&gt;&lt;br /&gt;                        &lt;/configuration&gt;&lt;br /&gt;                        &lt;goals&gt;&lt;br /&gt;                            &lt;goal&gt;${liquibase.goal}&lt;/goal&gt;&lt;br /&gt;                        &lt;/goals&gt;&lt;br /&gt;                    &lt;/execution&gt;&lt;br /&gt;                    &lt;execution&gt;&lt;br /&gt;                        &lt;id&gt;testdb&lt;/id&gt;&lt;br /&gt;                        &lt;phase&gt;process-resources&lt;/phase&gt;&lt;br /&gt;                        &lt;configuration&gt;&lt;br /&gt;                            &lt;contexts&gt;${test.database.contexts}&lt;br /&gt;                            &lt;/contexts&gt;&lt;br /&gt;                            &lt;url&gt;${test.database.url}&lt;/url&gt;&lt;br /&gt;                            &lt;username&gt;${test.database.user}&lt;/username&gt;&lt;br /&gt;                            &lt;password&gt;${test.database.password}&lt;br /&gt;                            &lt;/password&gt;&lt;br /&gt;                        &lt;/configuration&gt;&lt;br /&gt;                        &lt;goals&gt;&lt;br /&gt;                            &lt;goal&gt;${liquibase.goal}&lt;/goal&gt;&lt;br /&gt;                        &lt;/goals&gt;&lt;br /&gt;                    &lt;/execution&gt;&lt;br /&gt;                &lt;/executions&gt;&lt;br /&gt;            &lt;/plugin&gt;&lt;br /&gt;        &lt;/plugins&gt;&lt;br /&gt;    &lt;/build&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;/project&gt;&lt;/script&gt;&lt;p&gt;Параметры &lt;code&gt;app.database.xxx&lt;/code&gt; и &lt;code&gt;test.database.xxx&lt;/code&gt; можно определить в головном pom-файле проекта. Теперь накатывать изменения на обе БД можно одной командой:&lt;/p&gt;&lt;p&gt;&lt;code&gt;mvn process-resources&lt;/code&gt; либо &lt;code&gt;mvn clean install&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Чтобы сделать rollback обеих баз на несколько changeset-ов назад к команде следует добавить параметры:&lt;/p&gt;&lt;pre&gt;-Dliquibase.goal=rollback -Dliquibase.rollbackCount=1&lt;/pre&gt;&lt;p&gt;&lt;code&gt;liquibase.rollbackCount&lt;/code&gt; - определят кол-во &lt;code&gt;changeset&lt;/code&gt;-ов которые следует откатить.&lt;/p&gt;&lt;p&gt;Порой может возникнуть ситуация, когда требуется внести изменение в существующий changeset и заново накатить все changeset-и. Здесь поможет параметр:&lt;/p&gt;&lt;pre&gt;-Dliquibase.dropFirst=true&lt;/pre&gt;&lt;p&gt;Если потребуется работать с каждой БД отдельно, то необходимо использовать уже команды самого плагина liquibase:&lt;/p&gt;&lt;p&gt;&lt;code&gt;mvn liquibase:update&lt;/code&gt; либо &lt;code&gt;mvn liquibase:rollback&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Для тестовой БД добавляем параметр &lt;code&gt;-Ptestdb&lt;/code&gt; в командной строке.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2009/07/liquibase-hibernate-maven-1.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>2</thr:total></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#">aop</category><category domain="http://www.blogger.com/atom/ns#">javascript</category><category domain="http://www.blogger.com/atom/ns#">tapestry</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(&quot;An exception occurred in method &#39;&quot; + methodName + &quot;&#39; Error: &quot; + 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(&quot;An exception occurred in method &#39;&quot; + methodName + &quot;&#39; Error: &quot; + 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, &quot;simplemethod&quot;, function(args) {&lt;br /&gt;    var url = args[0];&lt;br /&gt;    url += &quot;?param=test&quot;;&lt;br /&gt;    args[0] = url;&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;sobject.simplemethod(&quot;http://test.com&quot;);&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Отмечу, что для использования данного механизма необоходма библиотека &lt;a href=&quot;http://prototypejs.org/&quot;&gt;Prototype&lt;/a&gt;&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2009/05/javascript-aop.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total></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=&quot;text-align:center;&quot;&gt;&lt;span style=&quot;color:red;&quot;&gt;1.&lt;/span&gt;&lt;img src=&quot;http://www.assembla.com/spaces/nkoksharov/documents/aEWqVghjGr3PKieJe5afGb/download/bean31.gif&quot;/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:red;&quot;&gt;2.&lt;/span&gt;&lt;img src=&quot;http://www.assembla.com/spaces/nkoksharov/documents/aITfaWhjGr3RI_eJe5afGb/download/bean32.gif&quot;/&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(&quot;JSESSIONID&quot;, 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(&quot;/&quot;);&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=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;http://www.assembla.com/spaces/nkoksharov/documents/aM7oOShjGr3RTLeJe5aVNr/download/bean33.gif&quot;/&gt;&lt;/div&gt;&lt;p&gt;Фильтр лучше всего поместить в самом начале цепочки фильтров, важно чтобы он был перед &lt;code&gt;springSecurityFilterChain&lt;/code&gt;. Следует отметить, что при таком решении http-cookie будет приходить пользователю на каждый https-запрос. Т.к. сложно опеределить при https-соединении создана ли cookie для http-канала.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2009/04/https-http-tomcat.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>3</thr:total></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#">approach</category><category domain="http://www.blogger.com/atom/ns#">tapestry</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=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;http://www.assembla.com/spaces/nkoksharov/documents/c1HlAOhjir3Q7ceJe5aVNr/download/tapestry-wrong-validation.gif&quot; border=&quot;0&quot; alt=&quot;&quot;/&gt;&lt;/div&gt;&lt;p&gt;Согласитесь, не очень красиво вставлять красный крест после каждого поля с ошибкой да еще и подсвечивать его содержимое. Во всяком случае меня это не устроило. Хотелось бы, чтобы ошибки выглядели примерно так:&lt;/p&gt;&lt;div style=&quot;text-align:center;&quot;&gt;&lt;img src=&quot;http://www.assembla.com/spaces/nkoksharov/documents/c9pS44hjir3Q_WeJe5aVNr/download/tapestry-validation.gif&quot; border=&quot;0&quot; alt=&quot;&quot;/&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(&quot;DefaultValidationDecorator&quot;, 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 = &quot;error_element&quot;;&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() + &quot;Error&quot;;&lt;br /&gt;            markupWriter.element(&quot;div&quot;,&lt;br /&gt;                    &quot;class&quot;, ERROR_ELEMENT_CLASS,&lt;br /&gt;                    &quot;id&quot;, 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;</description><link>http://nkoksharov.blogspot.com/2009/03/tapestry-51.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>0</thr:total></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#">approach</category><category domain="http://www.blogger.com/atom/ns#">spring</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(&quot;/spring/test/application-context.xml&quot;);&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(&quot;property&quot;, &quot;someValue&quot;);&lt;br /&gt;        BeanDefinition definition = builder.getBeanDefinition();&lt;br /&gt;        factory.registerBeanDefinition(&quot;testBean&quot;, definition);&lt;/b&gt;&lt;br /&gt;        &lt;br /&gt;        ... &lt;br /&gt;&lt;br /&gt;        TestBean bean = (TestBean) context.getBean(&quot;testBean&quot;);&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;</description><link>http://nkoksharov.blogspot.com/2009/02/bean-spring.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total></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 &quot;main&quot; 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=&quot;https://www.assembla.com/spaces/nkoksharov/documents/bsa7LW_O4r3z6QeJe5afGb/download/spring.schemas&quot;&gt;spring.schemas&lt;/a&gt; и &lt;a href=&quot;https://www.assembla.com/spaces/nkoksharov/documents/bkip4G_O4r3yjAeJe5aVNr/download/spring.handlers&quot;&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;</description><link>http://nkoksharov.blogspot.com/2009/02/maven-assembly-plugin-spring.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>8</thr:total></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#">logging</category><category domain="http://www.blogger.com/atom/ns#">tool</category><title>Инструмент для просмотра логов</title><description>&lt;p&gt;В повседневной работе довольно часто приходится смотреть логи от log4j. Прежде пользовался лишь обычным view-ром, пока не нашел довольно полезный open-source инструмент &lt;a href=&quot;http://mindtreeinsight.sourceforge.net/&quot;&gt;MindTree Insight&lt;/a&gt;, предназначенный именно для этой задачи.&lt;/p&gt;&lt;p&gt;Из настроек отмечу лишь, то что поле Preferences -&gt; Maintain Preferences -&gt; Primary Pattern должно содержать ту же строку форматирования, которую вы указали в настройках log4j.&lt;/p&gt;</description><link>http://nkoksharov.blogspot.com/2009/02/blog-post.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>8</thr:total></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; фильтра, управляющего параллельными сессиями. Такая &quot;неверная&quot;, в данном случае, конфигурация фильтров и приводит к потере SecurityContext-а. Потому завел на это дело багу - &lt;a href=&quot;http://jira.springframework.org/browse/SEC-1092&quot;&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;</description><link>http://nkoksharov.blogspot.com/2009/02/securitycontext-invalidate-http.html</link><author>noreply@blogger.com (Никита Кокшаров)</author><thr:total>1</thr:total></item></channel></rss>