<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2russianfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-8483146613266894707</atom:id><lastBuildDate>Wed, 01 Feb 2012 18:04:38 +0000</lastBuildDate><category>грабли</category><category>openid</category><category>postgresql</category><category>дизайн</category><category>качество кода</category><category>useful</category><category>лытдыбр</category><category>apache ant</category><category>аспекты</category><category>skype</category><category>rabbitvcs</category><category>gorm</category><category>dbus</category><category>sybase</category><category>блокировка</category><category>база данных</category><category>grails</category><category>фолксономия</category><category>spring roo</category><category>plugin</category><category>python</category><category>extension</category><category>rss</category><category>производительность</category><category>self-improvement</category><category>вложенная транзакция</category><category>performance</category><category>eclipse</category><category>nfs</category><category>basics</category><category>opera</category><category>транзакция</category><category>linux</category><category>обзор</category><category>extensions</category><category>foreign key</category><category>transaction</category><category>java</category><category>mysql</category><category>разработка</category><category>howto</category><category>best practices</category><category>GAE</category><category>bitly</category><category>constraint</category><category>google chrome</category><category>шопинг</category><category>oracle</category><category>android</category><category>groovy</category><category>книги</category><category>autofs</category><category>delicious</category><category>tiddlywiki</category><category>dropbox</category><category>chromium</category><category>article</category><category>testing</category><category>performance optimization</category><category>концерт</category><category>велосипед</category><category>svn</category><category>nautilus</category><title>Блог о разработке ПО</title><description>Небольшие заметки на айтишные темы.</description><link>http://atamanenko.blogspot.com/</link><managingEditor>noreply@blogger.com (Ginnungagap)</managingEditor><generator>Blogger</generator><openSearch:totalResults>50</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/atamanenko" /><feedburner:info uri="atamanenko" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/atamanenko" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://lenta.yandex.ru/settings.xml?name=feed&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://lenta.yandex.ru/i/addfeed.gif">?????? ? ??????.?????</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsalloy.com/?rss=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.newsalloy.com/subrss3.gif">Subscribe with NewsAlloy</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://download.attensa.com/app/get_attensa.html?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.attensa.com/blogs/attensa/WindowsLiveWriter/BadgeredintoBadges_10C02/attensa_feed_button5.gif">Subscribe with Attensa for Outlook</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.flurry.com/pushRssFeed.do?r=fb&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.flurry.com/images/flurry_rss_logo2.gif">Subscribe with Flurry</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2Fatamanenko" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-4840394302332756902</guid><pubDate>Fri, 27 Jan 2012 19:43:00 +0000</pubDate><atom:updated>2012-01-28T02:59:27.909+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">аспекты</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><title>Немножко магии от AspectJ</title><description>Наверно, вы уже сталкивались с таким понятием, как AOП - &lt;a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming"&gt;аспектно-ориентированное программирование&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Обычно, про него вспоминают, когда говорят про &lt;a href="http://static.springsource.org/spring/docs/current/reference/html/transaction.html#transaction-declarative-applying-more-than-just-tx-advice"&gt;декларативное использование транзакций&lt;/a&gt;,  про &lt;a href="https://docs.jboss.com/aop/1.0/aspect-library/reference/annotation15_security.html"&gt;проверку прав доступа&lt;/a&gt;, либо про &lt;a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/aop/interceptor/CustomizableTraceInterceptor.html"&gt;реализацию журналирования&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Но это не единственные области применения АОП.&lt;br /&gt;
&lt;br /&gt;
Я хочу показать ещё пару областей применения из реальных проектов:&lt;br /&gt;
&lt;br /&gt;
1. Модификация исходного кода для реализации дополнительных возможностей.&lt;br /&gt;
2. Принудительная проверка контракта между модулями.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Модификация исходного кода для реализации дополнительных возможностей&lt;/h4&gt;Предположим, что у нас есть модуль в приложении, который предоставляет нужную нам функциональность. С модулем всё в порядке, кроме одного - все его методы могут выбрасывать проверяемые исключения, что ведёт к ухудшению читаемости кода, так как вместо простого вызова метода:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:java;"&gt;service.doUsefulThing();
&lt;/pre&gt;&lt;br /&gt;
наш вызов превращается в &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:java;"&gt;try {
        service.doUsefulThing();
    } catch ( FirstServiceException e) {
        processException(e);
    } catch ( SecondServiceException e) {
        processException(e);
    }
&lt;/pre&gt;&lt;br /&gt;
Дополнительная проблема в том, что у модуля количество модулей 10+, количество методов также велико, что приводит к тому, что блоки &lt;tt&gt;try/catch&lt;/tt&gt; замусоривают код. Решение с использованием паттерна &lt;tt&gt;&lt;a href="https://en.wikipedia.org/wiki/Callback_(computer_programming)"&gt;Callback&lt;/a&gt;&lt;/tt&gt; также приведёт к замусориванию кода.&lt;br /&gt;
&lt;br /&gt;
&lt;h5&gt;Вариант решения проблемы с использованием AOP&lt;/h5&gt;Решение данной проблемы было таким - а что, если используя возможности AOP трансформировать проверяемое исключение в непроверяемое? Таким образом, мы сможем избавиться от скучной проверки на исключения в нашем коде, а для обработки исключения (ведь мы всегда его будем обрабатывать одинаково) достаточно будет использовать обработку исключения на верхнем уровне абстракции.&lt;br /&gt;
&lt;br /&gt;
Для более элегантного решения проблемы было решено добавить собственную аннотацию, которой нужно помечать метод, который использует сервис из "плохого" модуля.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:java;"&gt;package com.blogger.atamanenko;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
@Inherited
public @interface SuppressExceptions {
}
    
&lt;/pre&gt;&lt;br /&gt;
А также аспект, который бы делал всю нужную нам функциональность:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:java;"&gt;public aspect ExceptionSupressingAspect {

        declare soft :ServiceException:  execution(@com.blogger.atamanenko.annotation.SuppressExceptions * *.*(..));

}
&lt;/pre&gt;&lt;br /&gt;
Данный аспект делает в точности следующее: "Смягчает" исключение &lt;tt&gt;ServiceException&lt;/tt&gt; для метода, который помечен аннотацией &lt;tt&gt;@SuppressExceptions&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
Пример использования:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:java;"&gt;@SuppressExceptions
    protected Entity findEntity(final Identifiable id) {
        return entityService.findById(id);
    }
&lt;/pre&gt;&lt;h4&gt;Принудительная проверка контракта между модулями&lt;/h4&gt;Часто нам необходимо принудительно требовать выполнения каких-то архитектурных ограничений, например, контроллеры должны работать только с сервисами и им запрещено напрямую обращаться к базе данных.&lt;br /&gt;
&lt;br /&gt;
Для реализации таких проверок можно также использовать возможности AOP.&lt;br /&gt;
&lt;br /&gt;
Предположим, что в нашем приложении модуль сервиса выставляет наружу &lt;a href="http://java.sun.com/blueprints/corej2eepatterns/Patterns/TransferObject.html"&gt;DTO&lt;/a&gt; для работы, скрывая при этом классы модели. Для того, чтобы явно запретить доступ к классам и методам модели нам необходимо создать аспект, который бы вызывал ошибку компиляции при нарушении ограничения.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:java;"&gt;aspect ForbidAccessToModelAspect {

//      Full prohibition of access to model:
        pointcut accessModel(): call(* com.blogger.atamanenko.app.model..*.*(..));
        declare error: accessModel() : "Illegal call to model";

}
&lt;/pre&gt;&lt;br /&gt;
После объявления такого аспекта, мы получим ошибку компиляции, что, очевидным образом, приведёт к выполнению архитектурного ограничения.&lt;br /&gt;
&lt;br /&gt;
Если же нам необходимо разрешить доступ к одному пакету только из какого-то определённого другого, то мы можем модифицировать наш аспект следующим образом:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:java;"&gt;aspect ForbidAccessToModelAspect2 {

        pointcut accessModel(): call(* com.blogger.atamanenko.app.model.**.*(..));

        // Allow access to model from specific package for methods and constructors
        pointcut allowAccessModelFromSpecificPackage(): withincode(* com.blogger.atamanenko.app.allowedpackage..*.*(..));
        pointcut allowAccessModelFromSpecificPackage2(): withincode(com.blogger.atamanenko.app.allowedpackage..*.new(..));

        // forbid usage from any other methods.
        declare error: accessModel() &amp;&amp; !(allowAccessModelFromSpecificPackage() || allowAccessModelFromSpecificPackage()):"Illegal call to Model from forbidden package";

}

&lt;/pre&gt;&lt;br /&gt;
Такой аспект, созданный в нашем модуле запретит нам использовать классы модели из всех пакетов, кроме &lt;tt&gt;com.blogger.atamanenko.app.allowedpackage&lt;/tt&gt;&lt;br /&gt;
&lt;h4&gt;Сборка приложения&lt;/h4&gt;Файл аспекта нужно положить в каталог &lt;tt&gt;src/main/aspect&lt;/tt&gt;, а для сборки приложения необходимо использовать не стандартный Oracle &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/javac.html"&gt;Java Compiler&lt;/a&gt;, а &lt;a href="http://eclipse.org/aspectj/downloads.php"&gt;AspectJ compiler&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Пример конфигурации для &lt;a href="https://maven.apache.org/"&gt;Apache Maven&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:xml;"&gt;&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;aspectj-maven-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${aspectj-maven-plugin.version}&amp;lt;/version&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;complianceLevel&amp;gt;1.6&amp;lt;/complianceLevel&amp;gt;
        &amp;lt;aspectLibraries&amp;gt;
            &amp;lt;aspectLibrary&amp;gt;
                &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;spring-aspects&amp;lt;/artifactId&amp;gt;
            &amp;lt;/aspectLibrary&amp;gt;
        &amp;lt;/aspectLibraries&amp;gt;
        &amp;lt;verbose&amp;gt;true&amp;lt;/verbose&amp;gt;
    &amp;lt;/configuration&amp;gt;
    &amp;lt;executions&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;process-sources&amp;lt;/phase&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;compile&amp;lt;/goal&amp;gt;
                &amp;lt;goal&amp;gt;test-compile&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;&lt;h4&gt;Заключение&lt;/h4&gt;Вот в общем-то и всё. Я сознательно не стал описывать языковые конструкции аспектов, так как они подробно описаны в &lt;a href="https://www.eclipse.org/aspectj/doc/next/progguide/index.html"&gt;руководстве AspectJ&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-4840394302332756902?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=Akqe8Sj3NBo:UB-iIvciO0g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=Akqe8Sj3NBo:UB-iIvciO0g:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=Akqe8Sj3NBo:UB-iIvciO0g:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=Akqe8Sj3NBo:UB-iIvciO0g:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=Akqe8Sj3NBo:UB-iIvciO0g:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=Akqe8Sj3NBo:UB-iIvciO0g:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=Akqe8Sj3NBo:UB-iIvciO0g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=Akqe8Sj3NBo:UB-iIvciO0g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/Akqe8Sj3NBo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/Akqe8Sj3NBo/aspectj.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2012/01/aspectj.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-4866135366973502728</guid><pubDate>Thu, 21 Jul 2011 05:06:00 +0000</pubDate><atom:updated>2011-07-21T12:07:25.955+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">java</category><category domain="http://www.blogger.com/atom/ns#">basics</category><title>Немного о виртуальных методах в Java</title><description>Сегодня я хочу рассмотреть некоторые особенности переопределения методов в Java.  В java нельзя переопределить:   &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;поля класса&lt;/li&gt;
&lt;li&gt;конструкторы, инициализаторы класса&lt;/li&gt;
&lt;li&gt;статические методы&lt;/li&gt;
&lt;li&gt;статические поля&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Подробнее об этом можно прочитать в &lt;a href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#228745"&gt;Java Language Specification, §8.4.8&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Итак, в java все нестатические неприватные (то есть, &lt;tt&gt;protected&lt;/tt&gt;, &lt;tt&gt;package&lt;/tt&gt; и &lt;tt&gt;public&lt;/tt&gt;) методы являются виртуальными. Ключевое слово &lt;tt&gt;final&lt;/tt&gt; запрещает возможность дальнейшего переопределения метода в подклассах.  Рассмотрим следующий пример:  &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: java"&gt;public class A {
     int i = 3;
     int getI() {return i;}
}

public class B extends A{
     int i = 5;
     int getI() {return i;}
}

A a = new B();
System.out.println(a.i);
System.out.println(a.getI());

&lt;/pre&gt;&lt;br /&gt;
Вопрос: что выведет данный код?&lt;br /&gt;
Ответ: &lt;br /&gt;
1. Так как поля класса не наследуются, то у класса A своё поле i и у класса B тоже своё поле i. Так как для полей полиморфизм не действует, то при обращении a.i мы обращаемся к классу A, поэтому на экран будет выведено 3.&lt;br /&gt;
2. При вызове метода a.getI() у нас в дело вступает полиморфизм, поэтому будет вызван метод от класса, инстанс которого был создан. Соответственно, мы получим на выходе 5.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Другой пример:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: java"&gt;public class A {
     static int i = 3;
     static int getI() {return i;} 
}

public class B extends A{
     static int i = 5;
     static int getI() {return i;}
}

A a = new B();
System.out.println(a.i);
System.out.println(a.getI());

&lt;/pre&gt;&lt;br /&gt;
Статические поля и методы виртуальными не являются, поэтому оба вызова выведут нам 3.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-4866135366973502728?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=bHm0XPjFrWY:J7JYe-iFCo4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=bHm0XPjFrWY:J7JYe-iFCo4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=bHm0XPjFrWY:J7JYe-iFCo4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=bHm0XPjFrWY:J7JYe-iFCo4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=bHm0XPjFrWY:J7JYe-iFCo4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=bHm0XPjFrWY:J7JYe-iFCo4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=bHm0XPjFrWY:J7JYe-iFCo4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=bHm0XPjFrWY:J7JYe-iFCo4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/bHm0XPjFrWY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/bHm0XPjFrWY/java.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2011/07/java.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-2061609987772509071</guid><pubDate>Mon, 06 Dec 2010 17:11:00 +0000</pubDate><atom:updated>2012-01-28T19:22:35.277+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">bitly</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><category domain="http://www.blogger.com/atom/ns#">android</category><title>Сокращаем ссылки на андроиде</title><description>&lt;h5&gt;Вступление&lt;/h5&gt;Встала передо мной задача - сокращать ссылки перед тем, как отправлять их в Twitter. Для решения этой задачи я решил использовать &lt;a href="http://bit.ly"&gt;bit.ly&lt;/a&gt;, благо, их API внятный и простой.  &lt;h5&gt;Программируем!&lt;/h5&gt;Решение нарисовалось в виде следующего класса:  &lt;br /&gt;
&lt;pre class="brush: java"&gt;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;


/**
 * Helper class to work with bitly.
 *
 * @author Oleg Atamanenko
 * @since 06-Dec-2010 12:49:36
 */
public class Bitly {

    private static final String TAG = "Bitly";

    private static final String SHORTEN = "/v3/shorten";
    private static final String API_URL = "api.bit.ly";
    private static final String RESPONSE_FORMAT = "json";

    private String username;
    private String apiKey;


    public Bitly(String username, String apiKey) {
        this.username = username;
        this.apiKey = apiKey;
    }

    public String shorten(String longUrl) throws BitlyException {
        DefaultHttpClient httpClient = new DefaultHttpClient();

        try {
            List&lt;NameValuePair&gt; params = new ArrayList&lt;NameValuePair&gt;();
            params.add(new BasicNameValuePair("login", username));
            params.add(new BasicNameValuePair("apiKey", apiKey));
            params.add(new BasicNameValuePair("longUrl", longUrl));
            params.add(new BasicNameValuePair("format", RESPONSE_FORMAT));


            URI uri = URIUtils.createURI("http", API_URL, -1, SHORTEN, URLEncodedUtils.format(params, "UTF-8"), null);
            HttpGet request = new HttpGet(uri);

            Log.d(TAG, "Sending request: " + request.getURI());

            HttpResponse httpResponse = httpClient.execute(request);

            HttpEntity httpEntity = httpResponse.getEntity();
            String response = EntityUtils.toString(httpEntity);
            Log.i(TAG, "Bitly response is: " + response);
            httpResponse.getEntity().consumeContent();

            JSONObject jsonResponse = new JSONObject(response);

            checkForExceptions(jsonResponse);

            JSONObject data = jsonResponse.getJSONObject("data");
            return data.getString("url");

        } catch (ClientProtocolException e) {
            throw new BitlyException(e);
        } catch (IOException e) {
            throw new BitlyException(e);
        } catch (JSONException e) {
            throw new BitlyException(e);
        } catch (URISyntaxException e) {
            throw new BitlyException(e);
        }
    }

    private void checkForExceptions(JSONObject jsonResponse) throws JSONException, BitlyException {
        int statusCode = jsonResponse.getInt("status_code");
        if (statusCode != 200) {
            String message = jsonResponse.getString("status_txt");
            throw new BitlyException(message);
        }
    }

}
&lt;/pre&gt;  Конструктор класс принимает на вход следующие параметры: &lt;ul&gt;&lt;li&gt;&lt;code&gt;userName&lt;/code&gt; - имя пользователя bit.ly.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apiKey&lt;/code&gt; - Ключ для доступа к API, его можно узнать на &lt;a href="https://bit.ly/a/your_api_key"&gt;специальной страничке&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;a href="https://code.google.com/p/bitly-api/wiki/ApiDocumentation"&gt;Полная документация к Bit.ly API&lt;/a&gt; расположена на отдельном проекте в Google Code  &lt;h5&gt;Использование&lt;/h5&gt;Единственный метод, реализованный сейчас - это метод &lt;code&gt;shorten()&lt;/code&gt;. На вход требуется подать полную ссылку &lt;code&gt;longUrl&lt;/code&gt;, на выходе получается укороченная версия ссылки, либо кидается исключение с сообщением от bit.ly API.   &lt;h6&gt;Пример вызова&lt;/h6&gt;&lt;pre class="brush: java"&gt;
Bitly bitly = new Bitly(BITLY_USERNAME, BITLY_API_KEY);
String shortLink = bitly.shorten(link);
&lt;/pre&gt;&lt;br /&gt;
  &lt;h6&gt;Дальнейшие улучшения&lt;/h6&gt;Если перед вами стоит задача быстренько сократить ссылку - то вышеприведённого кода достаточно. Но если вам нужно полноценное решение, со всеми возможностями bit.ly - то посмотрите в сторону &lt;a href="https://code.google.com/p/bitlyj/"&gt;bitlyj&lt;/a&gt;. Правда, я не уверен, что оно взведётся под андроидом.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-2061609987772509071?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=o3n3ct1YK7c:7gbHuazO6Fk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=o3n3ct1YK7c:7gbHuazO6Fk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=o3n3ct1YK7c:7gbHuazO6Fk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=o3n3ct1YK7c:7gbHuazO6Fk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=o3n3ct1YK7c:7gbHuazO6Fk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=o3n3ct1YK7c:7gbHuazO6Fk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=o3n3ct1YK7c:7gbHuazO6Fk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=o3n3ct1YK7c:7gbHuazO6Fk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/o3n3ct1YK7c" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/o3n3ct1YK7c/blog-post.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/12/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-2698098713610160492</guid><pubDate>Fri, 21 May 2010 17:52:00 +0000</pubDate><atom:updated>2010-05-22T00:54:12.134+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">java</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><title>Удаление различных диакритических символов из строки</title><description>&lt;p&gt;Возникла проблема - каким образом заменить в строке символы из национальных кодировок на соответствующие им из латиницы.&lt;/p&gt;&lt;p&gt;Например, из строки explicación получить explicacion.&lt;/p&gt;&lt;pre class="brush: java"&gt;
package com.blogspot.atamanenko;

import java.text.Normalizer;
import java.text.Normalizer.Form;

public class StringNormalizer {

    public static String normalize(String string) {
        return Normalizer.normalize(string, Form.NFD)
            .replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
    }
}

&lt;/pre&gt;&lt;p&gt;Вызов &lt;a href='http://java.sun.com/javase/6/docs/api/java/text/Normalizer.html#normalize%28java.lang.CharSequence,%20java.text.Normalizer.Form%29'&gt;Normalizer.normalize&lt;/a&gt; проводит &lt;a href='http://www.unicode.org/reports/tr15/tr15-23.html'&gt;нормализацию&lt;/a&gt; входной строки. Последующий вызов регулярного выражения удаляет все диакритические знаки, полученные после нормализации.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-2698098713610160492?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=7VBQZTaxd-g:IEhsA6vdVmY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=7VBQZTaxd-g:IEhsA6vdVmY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=7VBQZTaxd-g:IEhsA6vdVmY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=7VBQZTaxd-g:IEhsA6vdVmY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=7VBQZTaxd-g:IEhsA6vdVmY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=7VBQZTaxd-g:IEhsA6vdVmY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=7VBQZTaxd-g:IEhsA6vdVmY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=7VBQZTaxd-g:IEhsA6vdVmY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/7VBQZTaxd-g" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/7VBQZTaxd-g/blog-post.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/05/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-369491908881359798</guid><pubDate>Fri, 21 May 2010 17:26:00 +0000</pubDate><atom:updated>2010-05-22T00:29:28.088+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">performance</category><category domain="http://www.blogger.com/atom/ns#">testing</category><title>Создание больших объёмов тестовых данных с помощью Databene Benerator</title><description>&lt;p&gt;Периодически необходимо решать задачу создания больших ( и не очень) объёмов тестовых данных для проведения различных видов тестирования - функционального, нагрузочного (тестирование стабильности и производительности). При этом часто получается так, что система на тестовых данных ведёт себя совсем иначе, чем на реальных данных. Причина кроется в том, что создать правдоподобные тестовые данные всегда достаточно сложно.&lt;/p&gt;
&lt;p&gt;Изучая данный вопрос я наткнулся на замечательный фреймворк - &lt;a href="http://databene.org/databene-benerator"&gt;Databene Benerator&lt;/a&gt;, основной целью создания которого как раз и является создание правдоподобных тестовых данных для проведения различных видов тестирования.&lt;/p&gt;
&lt;h4&gt;Установка&lt;/h4&gt;
&lt;p&gt;Установка фреймворка осуществляется двумя способами - как отдельное приложение и как плагин для Maven.&lt;/p&gt;
&lt;h5&gt;Установка под Maven&lt;/h5&gt;
&lt;p&gt;Для использования Databene benerator в проектах, использующих для сборки Apache Maven необходимо добавить в зависимости databene-benerator и сконфигурировать его.&lt;/p&gt;
&lt;pre class="brush: xml;"&gt;
&amp;lt;project xmlns=&amp;quot;http://maven.apache.org/POM/4.0.0&amp;quot; xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;
         xsi:schemaLocation=&amp;quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&amp;quot;&amp;gt;
  &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
  &amp;lt;groupId&amp;gt;com.myorganization&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;databene-benerator-test&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;
  &amp;lt;packaging&amp;gt;jar&amp;lt;/packaging&amp;gt;
  &amp;lt;name&amp;gt;data generation project&amp;lt;/name&amp;gt;
  &amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.databene&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;databene-benerator&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;0.5.9&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.databene&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;databene-webdecs&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;0.4.9&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;org.databene&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;databene-commons&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;0.4.9&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;mysql&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;mysql-connector-java&amp;lt;/artifactId&amp;gt;
      &amp;lt;version&amp;gt;5.1.6&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
  &amp;lt;/dependencies&amp;gt;
  &amp;lt;build&amp;gt;
    &amp;lt;resources&amp;gt;
      &amp;lt;resource&amp;gt;
        &amp;lt;directory&amp;gt;src/main/resources&amp;lt;/directory&amp;gt;
        &amp;lt;filtering&amp;gt;true&amp;lt;/filtering&amp;gt;
      &amp;lt;/resource&amp;gt;
    &amp;lt;/resources&amp;gt;
    &amp;lt;plugins&amp;gt;
      &amp;lt;plugin&amp;gt;
        &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
        &amp;lt;configuration&amp;gt;
          &amp;lt;encoding&amp;gt;UTF-8&amp;lt;/encoding&amp;gt;
          &amp;lt;source&amp;gt;1.5&amp;lt;/source&amp;gt;
          &amp;lt;target&amp;gt;1.5&amp;lt;/target&amp;gt;
        &amp;lt;/configuration&amp;gt;
      &amp;lt;/plugin&amp;gt;
      &amp;lt;plugin&amp;gt;
        &amp;lt;groupId&amp;gt;org.databene&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;maven-benerator-plugin&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;0.5.9&amp;lt;/version&amp;gt;
        &amp;lt;executions&amp;gt;
          &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;compile&amp;lt;/phase&amp;gt;
            &amp;lt;goals&amp;gt;
              &amp;lt;goal&amp;gt;generate&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
          &amp;lt;/execution&amp;gt;
        &amp;lt;/executions&amp;gt;
        &amp;lt;configuration&amp;gt;
          &amp;lt;descriptor&amp;gt;src/main/resources/benerator.ben.xml&amp;lt;/descriptor&amp;gt;
          &amp;lt;encoding&amp;gt;UTF-8&amp;lt;/encoding&amp;gt;
          &amp;lt;validate&amp;gt;true&amp;lt;/validate&amp;gt;
          &amp;lt;dbUrl&amp;gt;jdbc:mysql://localhost:3306/hrtool?useUnicode=true&amp;amp;characterEncoding=UTF-8&amp;lt;/dbUrl&amp;gt;
          &amp;lt;dbDriver&amp;gt;com.mysql.jdbc.Driver&amp;lt;/dbDriver&amp;gt;
          &amp;lt;dbSchema&amp;gt;database&amp;lt;/dbSchema&amp;gt;
          &amp;lt;dbUser&amp;gt;user&amp;lt;/dbUser&amp;gt;
          &amp;lt;dbPassword&amp;gt;password&amp;lt;/dbPassword&amp;gt;
        &amp;lt;/configuration&amp;gt;
        &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;log4j&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;log4j&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;1.2.13&amp;lt;/version&amp;gt;
            &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
          &amp;lt;/dependency&amp;gt;
          &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;poi&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;3.5-beta5&amp;lt;/version&amp;gt;
            &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
          &amp;lt;/dependency&amp;gt;
        &amp;lt;/dependencies&amp;gt;
      &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
  &amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;

&lt;/pre&gt;
&lt;p&gt;В конфигурации необходимо указать как минимум следующие параметры:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;span style=" font-family:'Courier New,courier';"&gt;dbUrl&lt;/span&gt; - строка подключения к базе данных в формате JDBC&lt;/li&gt;
&lt;li&gt;&lt;span style=" font-family:'Courier New,courier';"&gt;dbDriver&lt;/span&gt; - используемый драйвер базы данных&lt;/li&gt;
&lt;li&gt;&lt;span style=" font-family:'Courier New,courier';"&gt;dbSchema&lt;/span&gt; - имя схемы базы данных&lt;/li&gt;
&lt;li&gt;&lt;span style=" font-family:'Courier New,courier';"&gt;dbUser&lt;/span&gt; - имя пользователя, под которым подключаемся к базе данных&lt;/li&gt;
&lt;li&gt;&lt;span style=" font-family:'Courier New,courier';"&gt;dbPassword&lt;/span&gt; - пароль пользователя для подключения к базе данных&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Кроме использования файла pom.xml databene поддерживает возможность указания параметров в файле benerator.properties, что может быть удобно.&lt;/p&gt;
&lt;p&gt;Databene benerator обладает следующими возможностями:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Решение проблемы создания данных в общем виде. На данный момент поддерживаются XML и реляционные базы данных, но не за горами поддержка веб-сервисов, SAP и любых других систем через механизм расширений.&lt;/li&gt;
&lt;li&gt;Юзабилити. Databene-benerator позволяет упростить создание тестовых данных для сложной модели прикладной области.&lt;/li&gt;
&lt;li&gt;Обработка больших объёмов данных.&lt;/li&gt;
&lt;li&gt;Высокая производительность.&lt;/li&gt;
&lt;li&gt;Поддержка доменных областей.&lt;/li&gt;
&lt;li&gt;Качество данных. Фреймворк поддерживает проверку ограничений модели прикладной области.&lt;/li&gt;
&lt;li&gt;Компонентная, легкорасширяемая архитектура.&lt;/li&gt;
&lt;li&gt;Широкие возможности изменения и настройки генерации тестовых данных&lt;/li&gt;
&lt;li&gt;Создание тестовых данных с нуля.&lt;/li&gt;
&lt;li&gt;Импорт и анонимизация реальных данных.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;В комплекте идёт толковая, но, к сожалению, неполная &lt;a href="http://databene.org/download/databene-benerator-manual-0.6.0.pdf"&gt;документация&lt;/a&gt; по возможностям.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-369491908881359798?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=BDTwvM-RC_0:Wky07II5YGo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=BDTwvM-RC_0:Wky07II5YGo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=BDTwvM-RC_0:Wky07II5YGo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=BDTwvM-RC_0:Wky07II5YGo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=BDTwvM-RC_0:Wky07II5YGo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=BDTwvM-RC_0:Wky07II5YGo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=BDTwvM-RC_0:Wky07II5YGo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=BDTwvM-RC_0:Wky07II5YGo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/BDTwvM-RC_0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/BDTwvM-RC_0/databene-benerator.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>2</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/05/databene-benerator.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-690409162698701320</guid><pubDate>Sun, 11 Apr 2010 04:45:00 +0000</pubDate><atom:updated>2010-04-11T11:51:36.120+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">best practices</category><category domain="http://www.blogger.com/atom/ns#">gorm</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><category domain="http://www.blogger.com/atom/ns#">grails</category><title>Несколько слов о GORM</title><description>&lt;p&gt;В данной заметке хочу поделиться некоторыми моментами использования GORM.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html"&gt;GORM&lt;/a&gt; - это &lt;a href="http://en.wikipedia.org/wiki/Object-relational_mapping"&gt;ORM&lt;/a&gt;-фреймворк, используемый в &lt;a href="http://grails.org/"&gt;Grails&lt;/a&gt;. Реализован он поверх &lt;a href="http://hibernate.org/"&gt;Hibernate&lt;/a&gt;, но, при этом, с некоторыми отличными умолчаниями.&lt;/p&gt;
&lt;p&gt;Для разработчиков, знающих &lt;tt&gt;Hibernate&lt;/tt&gt;, рекомендую тщательно изучить &lt;tt&gt;GORM&lt;/tt&gt;, так как его поведение в некоторых случаях отлично от &lt;tt&gt;Hibernate&lt;/tt&gt;, что может приводить к различным сюрпризам.&lt;/p&gt;
&lt;h4&gt;&lt;/h4&gt;
&lt;h4&gt;Маппинг один-ко-многим&lt;/h4&gt;
&lt;p&gt;По умолчанию &lt;tt&gt;GORM&lt;/tt&gt; для связей &lt;em&gt;один ко многим&lt;/em&gt; (one-to-many) &lt;a href="http://www.grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html#5.2.1.2%20One-to-many" title="Grails will, by default, map this kind of relationship with a join table."&gt;создаёт таблицу-связку&lt;/a&gt;, которая обычно нужна только при связях между сущностями вида &lt;em&gt;многие ко многим&lt;/em&gt;. Чтобы исправить это поведение необходимо указать &lt;tt&gt;GORM&lt;/tt&gt;, чтобы он не создавал таблицу связку.&lt;/p&gt;
&lt;pre class="brush: java; highlight: [7]"&gt;
class Person implements Serializable {
  static hasMany = [
    scores: ScoreSheet
  ]
  
  static mapping = {
    scores joinTable: false
  };
}
&lt;/pre&gt;
&lt;h4&gt;Использование однонаправленных связей&lt;/h4&gt;
&lt;p&gt;Если в приложении используются двунаправленные связи и вероятность изменения сущности одновременно несколькими пользователями высокая, то лучше использовать однонаправленные связи для сущностей. Кроме того, лучше проектировать доменные классы таким образом, чтобы связь была не один-ко-многим, а многие к одному.&lt;/p&gt;
&lt;pre class="brush: java"&gt;
class Note implements Serializable {
  static belongsTo = [
    person: Person
  ]
}

class Person implements Serializable {
  // person fields.
}
&lt;/pre&gt;
&lt;p&gt;Для работы с &lt;tt&gt;Notes&lt;/tt&gt; необходимо использовать такие запросы: &lt;/p&gt;
&lt;pre class="brush: java"&gt;
  Note.findByPerson(person).each { -&amp;gt; };
&lt;/pre&gt;
&lt;p&gt; вместо &lt;/p&gt;
&lt;pre class="brush: java"&gt;
  person.notes.each { -&amp;gt; }
&lt;/pre&gt;
&lt;h4&gt;Маппинг иерархии классов доменных сущностей&lt;/h4&gt;
&lt;p&gt;GORM поддерживает только &lt;a href="http://www.grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html#5.2.3%20Inheritance%20in%20GORM"&gt;два варианта &lt;/a&gt;маппинга иерархии классов, в отличии от &lt;a href="http://docs.jboss.org/hibernate/stable/core/reference/en/html/inheritance.html"&gt;Hibernate&lt;/a&gt;: Таблица на всю иерархию (&lt;tt&gt;table-per-hierarchy&lt;/tt&gt;), или таблица на каждый подкласс (&lt;tt&gt;table-per-subclass&lt;/tt&gt;). У маппинга &lt;tt&gt;table-per-hierarchy&lt;/tt&gt; есть серьёзный недостаток - подклассы не могут иметь ненулевые поля. Поэтому, если этот недостаток критичен, то необходимо использовать маппинг &lt;tt&gt;table-per-subclass&lt;/tt&gt;.&lt;/p&gt;
&lt;pre class="brush: java; highlight: [7]"&gt;
class Payment {
  Long id
  Long version
  Integer amount

  static mapping = {
    tablePerHierarchy false
  }
}

class CreditCardPayment extends Payment {
  String cardNumber
}
 &lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-690409162698701320?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2r5xMrFT6iA:8rwX1HaStjY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2r5xMrFT6iA:8rwX1HaStjY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=2r5xMrFT6iA:8rwX1HaStjY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2r5xMrFT6iA:8rwX1HaStjY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=2r5xMrFT6iA:8rwX1HaStjY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2r5xMrFT6iA:8rwX1HaStjY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2r5xMrFT6iA:8rwX1HaStjY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=2r5xMrFT6iA:8rwX1HaStjY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/2r5xMrFT6iA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/2r5xMrFT6iA/gorm.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/04/gorm.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-3884458579727001911</guid><pubDate>Mon, 22 Mar 2010 17:50:00 +0000</pubDate><atom:updated>2010-03-23T00:01:36.964+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">skype</category><category domain="http://www.blogger.com/atom/ns#">python</category><category domain="http://www.blogger.com/atom/ns#">dbus</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><title>Общение со Skype через D-Bus на Python</title><description>&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;: в данной заметке описывается работа с программой Skype через D-Bus на Python.&lt;/p&gt;

&lt;h4&gt;Введение&lt;/h4&gt;
&lt;p&gt;Захотелось мне странного - когда я ухожу домой, мне нужно выключить amarok, kopete и Skype. Собственно, решено было через D-Bus отправлять вышеперечисленным приложениям релевантные сообщения.&lt;/p&gt;

&lt;h4&gt;Используем dbus-send&lt;/h4&gt;
&lt;p&gt;Сначала я использовал обычный dbus-send, что оформилось в виде следующего скрипта &lt;tt&gt;go2home&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="brush: bash"&gt;
#!/bin/sh

# Stop amarok
dbus-send --session --type=method_call --dest=org.kde.amarok /Player org.freedesktop.MediaPlayer.Stop

# Logout from kopete
dbus-send --session --type=method_call --dest=org.kde.kopete /Kopete org.kde.Kopete.disconnectAll 

# Logout from Skype
skypeapi.py 'SET USERSTATUS OFFLINE'

# Lock screen
dbus-send --session --type=method_call --dest=org.freedesktop.ScreenSaver /ScreenSaver org.freedesktop.ScreenSaver.Lock
&lt;/pre&gt;

Детали и параметры работы команды &lt;tt&gt;dbus-send&lt;/tt&gt; описаны в &lt;a href="http://dbus.freedesktop.org/doc/dbus-send.1.html"&gt;man-странице&lt;/a&gt;

&lt;h4&gt;Проблема со скайпом&lt;/h4&gt;
&lt;p&gt; Со скайпом пришлось немного повозиться, так как для работы с ним необходима постоянная сессия, что нельзя сделать с помощью &lt;tt&gt;dbus-send&lt;/tt&gt;. &lt;/p&gt;
&lt;p&gt;Прочитав &lt;a href="https://developer.skype.com/Docs/ApiDoc"&gt;описание протокола&lt;/a&gt; на сайте &lt;/p&gt;
&lt;p&gt; был создан нижеследующий скрипт: skypeapi.py&lt;/p&gt;
&lt;pre class="brush: python"&gt;
#!/usr/bin/env python
import dbus, sys

def main():
    remote_bus = dbus.SessionBus()
    
    # Check if skype is running.
    system_service_list = remote_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus').ListNames()
    skype_api_found = 0
    for service in system_service_list:
        if service=='com.Skype.API':
            skype_api_found = 1
            break
    if not skype_api_found:
        sys.exit('No running API-capable Skype found')

    # Get skype dbus api
    skype_service = remote_bus.get_object('com.Skype.API', '/com/Skype')

    # Connect to skype.
    answer = skype_service.Invoke('NAME SkypeApiClient')
    if answer != 'OK':
        sys.exit('Could not bind to Skype client')

    # Check if protocol is supported.
    answer = skype_service.Invoke('PROTOCOL 1')
    if answer != 'PROTOCOL 1':
        sys.exit('This test program only supports Skype API protocol version 1')

    # Invoke operations
    for arg in sys.argv:
        skype_service.Invoke(arg)
    
    return 0    

if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/pre&gt;
&lt;p&gt; При первом запуске скрипта появится скайповский диалог с вопросом, можно ли разрешить приложению доступ к скайпу. После нажатия на &amp;quot;Да&amp;quot; Skype добавит наш скрипт в разрешённые и мы сможем управлять скайпом.&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_y8p0_dtMJ38/S6ev4yVZkJI/AAAAAAAAA9Q/O87mz2Qnku0/s1600-h/skype.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 376px; height: 185px;" src="http://2.bp.blogspot.com/_y8p0_dtMJ38/S6ev4yVZkJI/AAAAAAAAA9Q/O87mz2Qnku0/s400/skype.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5451519264074338450" /&gt;&lt;/a&gt;

После этого для скрипта &lt;tt&gt;go2home&lt;/tt&gt; был создана иконка на панели.

&lt;h4&gt;Заключение&lt;/h4&gt;
Как видно, работа с DBus из Python проста и элегантна.

В качестве домашнего упражнения предлагаю написать скрипт, который после разблокирования экрана будет запускать все нужные приложения.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-3884458579727001911?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4U2nSJB8mjU:6UMHCW-u3JM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4U2nSJB8mjU:6UMHCW-u3JM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=4U2nSJB8mjU:6UMHCW-u3JM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4U2nSJB8mjU:6UMHCW-u3JM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=4U2nSJB8mjU:6UMHCW-u3JM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4U2nSJB8mjU:6UMHCW-u3JM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4U2nSJB8mjU:6UMHCW-u3JM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=4U2nSJB8mjU:6UMHCW-u3JM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/4U2nSJB8mjU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/4U2nSJB8mjU/skype-d-bus-python.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_y8p0_dtMJ38/S6ev4yVZkJI/AAAAAAAAA9Q/O87mz2Qnku0/s72-c/skype.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/03/skype-d-bus-python.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-1917754502779751980</guid><pubDate>Sun, 21 Mar 2010 14:54:00 +0000</pubDate><atom:updated>2010-03-27T23:47:25.599+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">howto</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><category domain="http://www.blogger.com/atom/ns#">tiddlywiki</category><title>Разработка макроса для TiddlyWiki</title><description>&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;: Пример разработки плагина для TiddlyWiki&lt;/p&gt;
&lt;h4&gt;Вступление&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.tiddlywiki.com/"&gt;TiddlyWiki&lt;/a&gt; - это вики-движок, полностью написанный на &lt;a href="http://en.wikipedia.org/wiki/JavaScript"&gt;JavaScript&lt;/a&gt; и хранящийся в одном файле (как сам движок, так и содержимое). Создатели позиционируют его как &amp;quot;переиспользуемую нелинейную персональную веб записную книжку&amp;quot;.&lt;/p&gt;
&lt;p&gt;Я давно использую TiddlyWiki в различных целях:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;По прямому назначению.&lt;/li&gt;
&lt;li&gt;Как систему &lt;a href="http://mgsd.tiddlyspot.com/#mGSD"&gt;GTD&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Как домашнюю страницу на компьютере.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Моя домашняя страница на компьютере - это, если говорить терминами TiddlyWiki, &lt;a href="http://en.wikipedia.org/wiki/Tiddler"&gt;тиддлер&lt;/a&gt;, содержащий ссылки на страницы, которые я часто посещаю.&lt;/p&gt;
&lt;p&gt;Для удобства использования я разработал пару стилей CSS, так что ссылки были крупными.&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_y8p0_dtMJ38/S6Y0rP2J0eI/AAAAAAAAA9I/xrZhzZf2ad0/s1600-h/tiddly-before.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 172px; height: 132px;" src="http://2.bp.blogspot.com/_y8p0_dtMJ38/S6Y0rP2J0eI/AAAAAAAAA9I/xrZhzZf2ad0/s400/tiddly-before.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5451102316571251170" /&gt;&lt;/a&gt;
&lt;h4&gt;Исходный код плагина&lt;/h4&gt;
&lt;p&gt;После длительного использования tiddlywiki в качестве домашней страницы я понял, что мне хочется, чтобы с каждой ссылкой была иконка сайта. Вручную вставлять картинки мне не хотелось, поэтому было решено написать собственный плагин.&lt;/p&gt;
&lt;pre class="brush: jscript"&gt;
/*{{{*/
version.extensions.faviconLinkMacro = {major: 0, minor: 1, revision: 0, date: new Date(2010,3,21)};
// Author: Oleg Atamanenko
config.macros.faviconLink = {}
config.macros.faviconLink.handler = function(place, macroName,  params, wikifier, paramString) {
  var linkBox = createTiddlyElement(place, &amp;quot;span&amp;quot;, null, &amp;quot;favIcon&amp;quot;, &amp;quot;&amp;quot;);

  var args = paramString.parseParams(&amp;quot;list&amp;quot;,null,true);
  var link = getParam(args, &amp;quot;link&amp;quot;, 'false');
  
  if (link != 'false'){
    urlParts = link.split('/');
    imgLink = urlParts[0] + &amp;quot;//&amp;quot; + urlParts[2] + &amp;quot;/favicon.ico&amp;quot;;

    var imgElement = createTiddlyElement(linkBox, &amp;quot;img&amp;quot;, null, &amp;quot;faviconImage&amp;quot;, &amp;quot;&amp;quot;);
    imgElement.src = imgLink;
    imgElement.width = 16;
    imgElement.height = 16;

    var linkTitle = getParam(args, &amp;quot;title&amp;quot;, 'false');

    if(linkTitle == 'false'){
      linkTitle = link;
    }
    var linkElement = createTiddlyElement(linkBox, &amp;quot;a&amp;quot;, null, &amp;quot;faviconLink&amp;quot;, linkTitle);
    linkElement.href = link;
    linkElement.target = '_blank';
  }
}

/*}}}*/
&lt;/pre&gt;
&lt;h4&gt; Установка плагина&lt;/h4&gt;
&lt;ol&gt;&lt;li&gt;Создаём новый тиддлер, называем его faviconLinkMacro&lt;/li&gt;
&lt;li&gt;Помечаем тиддлер тегом systemConfig, тогда TiddlyWiki при открытии страницы его подгрузит.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Использование плагина&lt;/h4&gt;
&lt;ol&gt;&lt;li&gt;Плагин создаёт новый макрос, faviconLink, с двумя параметрами link и title.&lt;/li&gt;
&lt;li&gt;Вызов &lt;tt&gt;&amp;lt;&amp;lt;faviconLink link:'https://mail.google.com/mail' title:'mail'&amp;gt;&amp;gt;&lt;/tt&gt; создаёт ссылку с картинкой с GMail&lt;/li&gt;
&lt;/ol&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_y8p0_dtMJ38/S6Y0qnki7nI/AAAAAAAAA9A/Ic3skFoirY8/s1600-h/favicon-sample.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 161px; height: 64px;" src="http://1.bp.blogspot.com/_y8p0_dtMJ38/S6Y0qnki7nI/AAAAAAAAA9A/Ic3skFoirY8/s400/favicon-sample.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5451102305759981170" /&gt;&lt;/a&gt;
&lt;h4&gt;Настройка визульных стилей плагина&lt;/h4&gt;
&lt;p&gt;Макрос создаёт следующие классы:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;span.favIcon&lt;/li&gt;
&lt;li&gt;img.faviconImage&lt;/li&gt;
&lt;li&gt;a.faviconLink&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt; Пример стилей&lt;/h4&gt;
&lt;pre class="brush: css"&gt;
.links span {
  display: block;
  position: relative;
  padding: 0.7em;
  margin: 0.3em;
  min-width: 6em;
  float:left;
  text-align: center;
  font-weight: bold;
  font-size: 1.5em;
  font-family: &amp;quot;Gill Sans MT&amp;quot;, &amp;quot;Candara&amp;quot;, &amp;quot;Arial&amp;quot;; 
  border: 2px solid [[ColorPalette::SecondaryLight]];
  background-color: [[ColorPalette::SecondaryPale]];
}

.links span:hover {
  border: 2px solid [[ColorPalette::SecondaryMid]];
  background-color: [[ColorPalette::SecondaryLight]];
  color: [[ColorPalette::PrimaryMid]];
}

.links img.faviconImage {
  position: relative;
  padding-right: 10px;
}

&lt;/pre&gt;
&lt;p&gt; Данный CSS необходимо добавить в тиддлер с именем StyleSheet&lt;/p&gt;
&lt;h4&gt;Пример тиддлера со ссылками&lt;/h4&gt;
&lt;pre class="brush: html"&gt;
{{links{
&amp;lt;&amp;lt;faviconLink link:'http://delicious.com/dark.schakal/2read' title:'2read'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'https://mail.google.com/mail' title:'mail'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://www.google.com/reader/view' title:'rss'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://atamanenko.blogspot.com/' title:'blog'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://feedburner.google.com/fb/a/myfeeds' title:'feedburner'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'https://www.google.com/analytics/settings/' title:'analytics'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://wave.google.com' title:'wave'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://www.blogger.com/home' title:'blogger'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://twitter.com/' title:'twitter'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'https://www.dropbox.com/home#/' title:'dropbox'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://translate.google.com/toolkit/' title:'translator'&amp;gt;&amp;gt; &amp;lt;&amp;lt;faviconLink link:'http://sibir.megafon.ru/sendsms/' title:'SendSMS'&amp;gt;&amp;gt;
}}}
&lt;/pre&gt;
Вышеприведённый код тиддлера создаёт следующую страницу:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_y8p0_dtMJ38/S6Y0qOLAO4I/AAAAAAAAA84/DFkORdMO8gE/s1600-h/favicon-sample-big.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 157px;" src="http://1.bp.blogspot.com/_y8p0_dtMJ38/S6Y0qOLAO4I/AAAAAAAAA84/DFkORdMO8gE/s400/favicon-sample-big.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5451102298941963138" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-1917754502779751980?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=kVFT8VuZjc4:m2MIxBoJJqQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=kVFT8VuZjc4:m2MIxBoJJqQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=kVFT8VuZjc4:m2MIxBoJJqQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=kVFT8VuZjc4:m2MIxBoJJqQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=kVFT8VuZjc4:m2MIxBoJJqQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=kVFT8VuZjc4:m2MIxBoJJqQ:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=kVFT8VuZjc4:m2MIxBoJJqQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=kVFT8VuZjc4:m2MIxBoJJqQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/kVFT8VuZjc4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/kVFT8VuZjc4/tiddlywiki.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_y8p0_dtMJ38/S6Y0rP2J0eI/AAAAAAAAA9I/xrZhzZf2ad0/s72-c/tiddly-before.png" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/03/tiddlywiki.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-3951746152835182554</guid><pubDate>Tue, 16 Mar 2010 15:52:00 +0000</pubDate><atom:updated>2010-03-16T22:12:48.464+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">производительность</category><category domain="http://www.blogger.com/atom/ns#">self-improvement</category><title>Борьба с убийцами производительности</title><description>&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;: Мой опыт борьбы с &amp;quot;убийцами производительности&amp;quot;.&lt;/p&gt;
&lt;h3&gt;Постановка проблемы&lt;/h3&gt;
&lt;p&gt; Иногда бывает так, что хочется отвлечься от работы, от поставленной задачи на какую-нибудь фигню, лишь бы не заниматься текущей задачей. У меня это чаще всего сводилось к тому, что я начинал читать что-нибудь в сети (&lt;a href="http://www.google.com/reader/view/" target="_blank"&gt;Google Reader&lt;/a&gt;, &lt;a href="http://habrahabr.ru/"&gt;Habrahabr&lt;/a&gt;, &lt;a href="http://www.linux.org.ru/"&gt;LOR&lt;/a&gt;, etc). &lt;/p&gt;
&lt;p&gt;Истоки проблемы могут быть абсолютно разными, например: &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Долгий запуск среды разработки &lt;/li&gt;
&lt;li&gt;Долгая сборка проекта&lt;/li&gt;
&lt;li&gt;Нудная задача&lt;/li&gt;
&lt;li&gt;Неинтересная задача&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Все эти причины спокойно могут приводить к потере производительности, причём неявным образом, например - &amp;quot;А посижу пока я на хабре, пока полная сборка идёт&amp;quot;, в итоге после 20 минут обнаруживается, что сборка закончилась уже 15 минут назад.&lt;/p&gt;
&lt;p&gt;Так как у меня тайм-киллер вполне простой - интернет, то и решение вполне простое.&lt;/p&gt;
&lt;p&gt;Вот список действий, что я сделал:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Удалил быстрые браузеры, теперь у меня только &lt;a href="http://www.mozilla.com/firefox"&gt;Mozilla Firefox&lt;/a&gt; под Linux и страшный &lt;a href="http://www.microsoft.com/windows/ie/ie6/downloads/default.mspx"&gt;Internet Explorer 6.0 под Windows&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Забанил сайты которые не нужны для работы, но на которых я провожу много времени.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Сначала я сделал радикальный бан - внёс в &lt;tt&gt;/etc/hosts&lt;/tt&gt; эти сайты:&lt;/p&gt;
&lt;pre class="brush: bash"&gt;
127.0.0.1 www.linux.org.ru linux.org.ru
127.0.0.1 www.habrahabr.ru habrahabr.ru
127.0.0.1 www.lorquotes.ru lorquotes.ru
&lt;/pre&gt;
&lt;p&gt; Но потом обнаружилось, что есть ещё &lt;a href="http://www.google.com/reader/view/"&gt;Google Reader&lt;/a&gt;, который также отнимает много времени (у меня было до 200 подписок, что давало до 300 новых статей в сутки). Проблему с &lt;a href="http://www.google.com/reader/view/"&gt;Google Reader&lt;/a&gt; я решал поэтапно: &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Почистил список лент от юмора, который обновляется практически ежедневно, является пожирателем трафика и времени.&lt;/li&gt;
&lt;li&gt;После этого я почистил список лент от лент, которые достаточно интересные, но при этом без них можно спокойно обходиться (Например, &lt;a href="http://aseigo.blogspot.com/"&gt;блог Аарона Сейго&lt;/a&gt;, одного из главных разработчиков &lt;a href="http://www.kde.org/"&gt;KDE&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Вышеперечисленные действия помогли мне сократить список фидов с 200 сначала до 100 с небольшим, а затем и до 65. Но я понимал, что это ещё не предел и есть к чему стремиться, поэтому решил перейти к использованию оффлайнового &lt;a href="http://www.whatisrss.com/"&gt;RSS&lt;/a&gt;-&lt;a href="http://en.wikipedia.org/wiki/RSS_Reader"&gt;клиента&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Для этого я экспортировал &lt;a href="http://www.opml.org/"&gt;OPML&lt;/a&gt;-файл с подписками из &lt;a href="http://www.google.com/reader/view/"&gt;Google Reader&lt;/a&gt;, удалил все ленты (к сожалению, &lt;a href="http://www.google.com/support/forum/p/reader/thread?tid=53c0b347fa8cd843&amp;hl=en"&gt;удалить аккаунт&lt;/a&gt; на &lt;a href="http://www.google.com/reader/view/"&gt;Google Reader&lt;/a&gt; невозможно, только полностью Google Account) и импортировал на &lt;a href="http://userbase.kde.org/Akregator"&gt;Akregator&lt;/a&gt; на домашней машинке. Это помогло мне аккумулировать все чтения ленты новостей в один временной промежуток - вечером, дома.&lt;/p&gt;
&lt;p&gt;Но, как оказалось, ещё есть к чему стремиться и гугление на просторах сети дало свои результаты: &lt;a href="http://www.proginosko.com/leechblock.html"&gt;&lt;strong&gt;LeechBlock&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;LeechBlock это расширение для Mozilla Firefox, которое позволяет блокировать отдельные сайты на различные промежутки времени.&lt;/p&gt;
&lt;p&gt;Собственно, начав им пользоваться, я понял, что это именно то, что мне нужно:&lt;/p&gt;
&lt;p&gt; &lt;a href="http://picasaweb.google.com/lh/photo/GiOXKIEw3fz0uoNMZxGTlg?authkey=Gv1sRgCOCWw8r8gI-DRQ&amp;feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_y8p0_dtMJ38/S56FpVJKVHI/AAAAAAAAA8Q/e6hTfBRPkRo/s800/leechblock.png" title="Leechblock Settings UI" alt="Leechblock Settings UI" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt; Возможности данного расширения следующие: &lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Блокировка сайтов различные периоды времени&lt;/li&gt;
&lt;li&gt;Блокировка сайтов по временному диапазону, в том числе по дням недели.&lt;/li&gt;
&lt;li&gt;Поддержка нескольких наборов сайтов для блокировки&lt;/li&gt;
&lt;li&gt;Возможность быстрого добавления сайта в список для блокировки &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Конечно, данное расширение не является панацеей, его нужно использовать в комплексе с другими средствами.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-3951746152835182554?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=QsGASReJU8g:4cBWyTKeGlU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=QsGASReJU8g:4cBWyTKeGlU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=QsGASReJU8g:4cBWyTKeGlU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=QsGASReJU8g:4cBWyTKeGlU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=QsGASReJU8g:4cBWyTKeGlU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=QsGASReJU8g:4cBWyTKeGlU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=QsGASReJU8g:4cBWyTKeGlU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=QsGASReJU8g:4cBWyTKeGlU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/QsGASReJU8g" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/QsGASReJU8g/blog-post.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_y8p0_dtMJ38/S56FpVJKVHI/AAAAAAAAA8Q/e6hTfBRPkRo/s72-c/leechblock.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/03/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-4998232370776089357</guid><pubDate>Sat, 06 Feb 2010 17:31:00 +0000</pubDate><atom:updated>2010-02-09T20:50:15.660+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">производительность</category><category domain="http://www.blogger.com/atom/ns#">java</category><category domain="http://www.blogger.com/atom/ns#">performance optimization</category><category domain="http://www.blogger.com/atom/ns#">база данных</category><category domain="http://www.blogger.com/atom/ns#">grails</category><category domain="http://www.blogger.com/atom/ns#">groovy</category><title>Список для проверки при оптимизации Grails приложений</title><description>Выкладываю ниже список задач, которые нужно/можно выполнить для оптимизации приложения, написанного на Grails, может кому пригодится.&lt;br /&gt;
&lt;h4&gt;Тестирование проведённых оптимизаций &lt;/h4&gt;Первым делом необходимо разработать критерии проверки, которые позволят оценить эффективность проведённых оптимизаций.&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Установить &lt;a href="http://code.google.com/p/javamelody/"&gt;Java Melody&lt;/a&gt; &lt;a href="http://www.grails.org/plugin/grails-melody"&gt;плагин&lt;/a&gt; для Grails для проведения анализа. &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Разработать скрипты для проведения нагрузочного тестирования.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Прогнать скрипты.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Проанализировать результаты Java Melody, выявить узкие места, произвести нужные оптимизации.&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Общие оптимизации &lt;/h4&gt;Очень часто обновление до последней версии используемых библиотек попутно улучшает производительность.&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Обновить Java до последней версии&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Обновить Groovy до последней версии.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Обновить Grails до последней версии.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Обновить jQuery до последней версии.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Оптимизировать настройки виртуальной машины Java (например, -server -Xmx270m -Xms270m -XX:MaxPermSize=80m -Xverify:none -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy -XX:SurvivorRatio=4 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31 -XX:+AggressiveOpts)&lt;br /&gt;
a. http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html&lt;br /&gt;
Вообще, по оптимизации JVM написаны целые книги, так что не буду здесь останавливаться.&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Оптимизация клиентской части &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Настроить &lt;a href="http://tomcat.apache.org/tomcat-5.5-doc/config/context.html"&gt;кэширование статических ресурсов&lt;/a&gt; в Томкате.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Настроить &lt;a href="http://tomcat.apache.org/tomcat-5.5-doc/config/http.html"&gt;сжатие статических ресурсов&lt;/a&gt; в Томкате.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;(поставить прокси на nginx для кэширования статических ресурсов ?)&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Установить плагины для Firebug, которые оценят производительность клиентской части.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/speed/page-speed"&gt;PageSpeed&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Проанализировать страницы веб-сайта с их помощью&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Провести предлагаемые оптимизации.&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Оптимизация клиентской части приложений, написанных на Grails &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Установить плагин &lt;a href="http://www.grails.org/plugin/ui-performance"&gt;UI Performance&lt;/a&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Провести оптимизацию с использованием плагина UI Performance &lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Оптимизация базы данных &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Оптимизировать производительность сервера базы данных (SQL Server) &lt;br /&gt;
a. http://www.sql-server-performance.com/tips/all_main.aspx &lt;br /&gt;
a. http://msdn.microsoft.com/en-us/library/ms998577.aspx&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Проверить наличие индексов в базе данных, добавить необходимые&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Найти неоптимальные запросы в БД, оптимизировать&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Оптимизация работы с базой данных &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Обновить версию JDBC-драйвера&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Настроить кэширование в Hibernate.&lt;br /&gt;
Ресурсов в сети предостаточно, навскидку &lt;a href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html"&gt;вот&lt;/a&gt; &lt;a href="http://www.devx.com/dbzone/Article/29685/1954"&gt; парочка&lt;/a&gt;.&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Оптимизация серверной части приложения &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Произвести профилирование приложения (использовать Visual VM, YJP Profiler), найти узкие места и оптимизировать их.&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Оптимизация серверной части приложения (опционально) &lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Установить плагин &lt;a href="http://www.grails.org/plugin/perf4j"&gt;Perf4J&lt;/a&gt; для грэйлза &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Добавить необходимые счётчики в код&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Прогнать нагрузочные тесты, найти узкие места, оптимизировать.&lt;/li&gt;
&lt;/ol&gt;&lt;h4&gt;Заключение&lt;/h4&gt;В целом, в данном списке нет ничего нового, это всего лишь компиляция публично доступных материалов. Тем не менее, решил это опубликовать, чтобы сэкономить время другим.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-4998232370776089357?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mJA8ySWqDeQ:nbl2D77LyFo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mJA8ySWqDeQ:nbl2D77LyFo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=mJA8ySWqDeQ:nbl2D77LyFo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mJA8ySWqDeQ:nbl2D77LyFo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=mJA8ySWqDeQ:nbl2D77LyFo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mJA8ySWqDeQ:nbl2D77LyFo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mJA8ySWqDeQ:nbl2D77LyFo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=mJA8ySWqDeQ:nbl2D77LyFo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/mJA8ySWqDeQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/mJA8ySWqDeQ/grails.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>6</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/02/grails.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-2319239558066259542</guid><pubDate>Thu, 04 Feb 2010 19:20:00 +0000</pubDate><atom:updated>2010-02-09T13:18:00.017+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">mysql</category><category domain="http://www.blogger.com/atom/ns#">sybase</category><category domain="http://www.blogger.com/atom/ns#">база данных</category><category domain="http://www.blogger.com/atom/ns#">транзакция</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><category domain="http://www.blogger.com/atom/ns#">вложенная транзакция</category><category domain="http://www.blogger.com/atom/ns#">oracle</category><title>Вложенные транзакции в базах данных</title><description>Summary: Некоторые особенности вложенных транзакций.&lt;br /&gt;
&lt;br /&gt;
Иногда бывает так, что при обработке запроса необходимо открыть ещё одну транзакцию в рамках текущей транзакции. Это называется вложенной транзакцией. &lt;br /&gt;
&lt;br /&gt;
Очень многие базы данных не поддерживают вложенные транзакции вообще, например, MySQL и Oracle. А те, что поддерживают, делают это на минимальном уровне, например, Sybase поддерживает только псевдовложенные транзакции.&lt;br /&gt;
&lt;br /&gt;
Вложенные транзакции могут быть следующих видов:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Псевдо-вложенные транзакции&lt;/li&gt;
&lt;li&gt;Вложенная субтранзакция&lt;/li&gt;
&lt;li&gt;Вложенная независимая транзакция&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;h3&gt;Псевдовложенные транзакции&lt;/h3&gt;&lt;br /&gt;
Этот тип транзакций поддерживается базой данных Sybase.&lt;br /&gt;
Псевдо-вложенные транзакции позволяют использовать транзакции одна внутри другой, при этом не реализуя механизм вложенных транзакций. Суть состоит в том, что имеется счётчик уровня вложенности транзакций, который увеличивается на 1 при каждом вызове &lt;tt&gt;BEGIN TRANSACTION&lt;/tt&gt; и уменьшается на 1 при каждом вызове &lt;tt&gt;COMMIT&lt;/tt&gt;. Когда счётчик становится равным 0 происходит коммит всех произведённых изменений.&lt;br /&gt;
&lt;br /&gt;
Если во время проведения транзакции происходит ошибка, то вызывается &lt;tt&gt;ROLLBACK&lt;/tt&gt;, но здесь есть загвоздка - что делать с последующими вызовами &lt;tt&gt;BEGIN TRANSACTION&lt;/tt&gt;  и &lt;tt&gt;COMMIT&lt;/tt&gt;. Оптимальным решением является кидание исключения, но это необходимо отслеживать и учитывать на стадии проектирования и реализации, чтобы не возникло неприятных ситуаций при эксплуатации.&lt;br /&gt;
&lt;br /&gt;
Псевдовложенные транзакции являются самым простым способом реализации вложенных транзакций.&lt;br /&gt;
&lt;br /&gt;
Пример псевдотранзакции для базы данных Sybase:&lt;br /&gt;
&lt;pre class="brush: sql"&gt;begin tran
    select @@trancount
    /* @@trancount = 1 */
    begin tran
        select @@trancount
        /* @@trancount = 2 */
        begin tran
            select @@trancount
            /* @@trancount = 3 */
        commit tran
    commit tran
commit tran
select @@trancount
/* @@ trancount = 0 */
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Вложенная субтранзакция&lt;/h3&gt;&lt;br /&gt;
Данный вид вложенных транзакций решает проблему с обработкой ошибок при &lt;tt&gt;ROLLBACK&lt;/tt&gt;. &lt;br /&gt;
При коммите вложенной транзакции происходит коммит, но он не обладает свойством &lt;tt&gt;Durability&lt;/tt&gt; - окончательный результат зависит от результата внешней транзакции - если внешняя транзакция заканчивается успешно, то и результат внутренней транзакции также фиксируется. Если же при фиксации изменений внешней транзакции происходит ошибка, то внутренняя транзакция также откатывается. Кроме того, необходимо обратить внимание на то, что если внутренняя транзакция заканчивается неуспешно, то внешняя транзакция может закончиться успешно, если не выкинуть исключение наружу. Таким образом, для данного вида вложенных транзакций необходимо выкидывать исключение наружу, для того, чтобы прервать и откатить внешнюю транзакцию в случае ошибки.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Вложенная независимая транзакция&lt;/h3&gt;Особенность данного вида вложенных транзакций заключается в том, что после создания вложенной транзакции она является полностью независимой от транзакции, внутри которой она была создана. Её результаты фиксируются и откатываются независимо от внешней транзакции.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Вложенные транзакции и принципы ACID&lt;/h3&gt;Вложенные транзакции выглядят достаточно подозрительными в том смысле, что они могут нарушать принципы ACID: &lt;br /&gt;
1. Вложенная субтранзакция может нарушать принцип &lt;tt&gt;Durability&lt;/tt&gt;, так как уже зафиксированные изменения могут откатиться в случае отката внешней транзакции.&lt;br /&gt;
2. Вложенная независимая транзакция может нарушать принципы &lt;tt&gt;Atomicity&lt;/tt&gt; и &lt;tt&gt;Consistency&lt;/tt&gt; при возникновении ошибок во внешней или вложенной транзакции.&lt;br /&gt;
&lt;br /&gt;
Подробнее о принципах &lt;abbr title="Atomicity, Consistency, Isolation, Durability"&gt;ACID&lt;/abbr&gt; можно прочитать в &lt;a href="http://atamanenko.blogspot.com/2009/04/blog-post_24.html"&gt;этом посте&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Заключение&lt;/h3&gt;Я считаю, что следует избегать ситуаций, когда нужны вложенные транзакции. Кроме того, спецификация Java EE &lt;a href="http://java.sun.com/javaee/6/docs/api/javax/ejb/TransactionAttributeType.html"&gt;не поддерживает&lt;/a&gt; вложенные транзакции.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-2319239558066259542?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=t_-_6JIfVv8:TNCqi4AbJ10:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=t_-_6JIfVv8:TNCqi4AbJ10:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=t_-_6JIfVv8:TNCqi4AbJ10:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=t_-_6JIfVv8:TNCqi4AbJ10:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=t_-_6JIfVv8:TNCqi4AbJ10:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=t_-_6JIfVv8:TNCqi4AbJ10:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=t_-_6JIfVv8:TNCqi4AbJ10:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=t_-_6JIfVv8:TNCqi4AbJ10:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/t_-_6JIfVv8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/t_-_6JIfVv8/blog-post.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>9</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2010/02/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-3731593876741901400</guid><pubDate>Mon, 14 Dec 2009 17:14:00 +0000</pubDate><atom:updated>2009-12-14T23:17:43.993+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">autofs</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">howto</category><category domain="http://www.blogger.com/atom/ns#">nfs</category><title>Настройка autofs для монтирования NFS-ресурсов</title><description>Summary: В данной заметке описана настройка autofs для доступа к сетевым ресурсам, доступным по NFS.&lt;br /&gt;
&lt;br /&gt;
Последние несколько лет (с тех пор, как количество компьютеров дома стало больше одного) возникла проблема беспрепятственного доступа к данным, хранящимся на одном компьютере с другого.&lt;br /&gt;
&lt;br /&gt;
Было найдено самое простое решение - &lt;a href="http://en.wikipedia.org/wiki/Network_File_System_(protocol)"&gt;NFS&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Как выяснилось позже это было не самое удачное решение - ноутбук не всегда находился дома, а, значит, домашние сетевые ресурсы не всегда доступны. Соответственно, при загрузке операционной системы происходили задержки из-за поиска компьютера с сетевыми ресурсами. Не очень удобно, но жить можно.&lt;br /&gt;
&lt;br /&gt;
Решил окончательно разобраться с этим и начал искать решение. Оно оказалось на поверхности и затронуло только клиента ресурсов, то есть ноутбук. Решение называется &lt;tt&gt;&lt;a href="http://www.autofs.org/"&gt;autofs&lt;/a&gt;&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
1. Устанавливаем &lt;tt&gt;autofs&lt;/tt&gt;.&lt;br /&gt;
&lt;pre class="brush: bash"&gt;sudo aptitude install autofs5 nfs-common
&lt;/pre&gt;&lt;br /&gt;
2. Производим настройку.&lt;br /&gt;
Редактируем файл &lt;tt&gt;/etc/auto.master&lt;/tt&gt;. Расскомментируем строку, содержащую строки &lt;tt&gt;/net -hosts&lt;/tt&gt;:&lt;br /&gt;
&lt;pre class="brush: bash"&gt;/net -hosts
+auto.master
&lt;/pre&gt;&lt;br /&gt;
3. В файл &lt;tt&gt;&lt;a href="http://en.wikipedia.org/wiki/Hosts_file"&gt;/etc/hosts&lt;/a&gt;&lt;/tt&gt; можно внести адреса серверов с NFS-ресурсами (для того, чтобы избежать &lt;a href="http://en.wikipedia.org/wiki/Domain_Name_System"&gt;DNS&lt;/a&gt;-запросов). В моём случае:&lt;br /&gt;
&lt;pre class="brush: plain"&gt;192.168.18.1 server
&lt;/pre&gt;&lt;br /&gt;
4. Перезапускаем сервис &lt;tt&gt;autofs&lt;/tt&gt;&lt;br /&gt;
&lt;pre class="brush: plain"&gt;sudo service autofs restart
&lt;/pre&gt;&lt;br /&gt;
5. Теперь открываем в файловом браузер адрес &lt;tt&gt;/net/server&lt;/tt&gt; и видим его сетевые ресурсы, доступные для данного клиента.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_y8p0_dtMJ38/SyZxDikTDxI/AAAAAAAAAvk/LTr3KsGATGk/s1600-h/Screenshot-media+-+administrilo+de+dosieroj.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_y8p0_dtMJ38/SyZxDikTDxI/AAAAAAAAAvk/LTr3KsGATGk/s320/Screenshot-media+-+administrilo+de+dosieroj.png" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
6. Пользуемся.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-3731593876741901400?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=zGQXnv_qaLE:2bD8F1bWd-c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=zGQXnv_qaLE:2bD8F1bWd-c:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=zGQXnv_qaLE:2bD8F1bWd-c:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=zGQXnv_qaLE:2bD8F1bWd-c:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=zGQXnv_qaLE:2bD8F1bWd-c:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=zGQXnv_qaLE:2bD8F1bWd-c:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=zGQXnv_qaLE:2bD8F1bWd-c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=zGQXnv_qaLE:2bD8F1bWd-c:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/zGQXnv_qaLE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/zGQXnv_qaLE/autofs-nfs.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_y8p0_dtMJ38/SyZxDikTDxI/AAAAAAAAAvk/LTr3KsGATGk/s72-c/Screenshot-media+-+administrilo+de+dosieroj.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/12/autofs-nfs.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-3019070198632525019</guid><pubDate>Tue, 08 Dec 2009 17:43:00 +0000</pubDate><atom:updated>2009-12-08T23:43:24.960+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">delicious</category><category domain="http://www.blogger.com/atom/ns#">chromium</category><category domain="http://www.blogger.com/atom/ns#">google chrome</category><title>Расширение для работы с Delicious доступно на сайте расширений Google Chrome</title><description>Итак, &lt;a href="code.google.com/p/chromium-delicious-extension/"&gt;расширение для работы с Delicious&lt;/a&gt; доступно на &lt;a href="https://chrome.google.com/extensions"&gt;сайте расширений Google Chrome&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://chrome.google.com/extensions/detail/anlkjppofaicbdanhhpbbogknfodfhfm/"&gt;Посмотреть и установить&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Новые версии расширения буду закачить на этот сайт.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-3019070198632525019?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=VKLttRWj7Xc:qsfFsOBHJcw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=VKLttRWj7Xc:qsfFsOBHJcw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=VKLttRWj7Xc:qsfFsOBHJcw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=VKLttRWj7Xc:qsfFsOBHJcw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=VKLttRWj7Xc:qsfFsOBHJcw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=VKLttRWj7Xc:qsfFsOBHJcw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=VKLttRWj7Xc:qsfFsOBHJcw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=VKLttRWj7Xc:qsfFsOBHJcw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/VKLttRWj7Xc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/VKLttRWj7Xc/delicious-google-chrome.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/12/delicious-google-chrome.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-1971197807983104412</guid><pubDate>Tue, 08 Dec 2009 17:17:00 +0000</pubDate><atom:updated>2009-12-08T23:29:02.438+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">useful</category><category domain="http://www.blogger.com/atom/ns#">dropbox</category><title>Использование Dropbox для хранения конфигурационных файлов</title><description>Есть такой замечательный сервис - &lt;a href="https://www.getdropbox.com/"&gt;Dropbox&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
Этот сервис является онлайн-хранилищем ваших данных. Работает очень просто:&lt;br /&gt;
&lt;br /&gt;
1. Вы регистрируетесь в сервисе.&lt;br /&gt;
2. Устанавливаете клиент&lt;br /&gt;
3. Пользуетесь.&lt;br /&gt;
&lt;br /&gt;
Недавно я понял, что этот сервис можно легко и непринуждённо использовать не только для хранения различных документов и фотографий, а также и конфигурационных файлов приложений, что существенно упрощает жизнь, когда у вас несколько рабочих машин (например, у меня домашний компьютер, ноутбук, а также ещё и рабочий) - необходимо каким-то образом синхронизовать конфиги при изменении их на одной из машине с другими.&lt;br /&gt;
&lt;br /&gt;
Итак, что я сделал.&lt;br /&gt;
&lt;br /&gt;
Нижеприведённые действия я делал на одной машине:&lt;br /&gt;
1. Создал каталог &lt;tt&gt;ConfigFiles&lt;/tt&gt;&lt;br /&gt;
2. Переместил в него свои конфигурационные файлы (&lt;tt&gt;~/.zshrc&lt;/tt&gt;, &lt;tt&gt;~/.zsh&lt;/tt&gt;, &lt;tt&gt;~/.vimrc&lt;/tt&gt;, &lt;tt&gt;~/.vim&lt;/tt&gt;)&lt;br /&gt;
3. Из каталога Dropbox сделал соответствующие симлинки в домашнем каталоге.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_y8p0_dtMJ38/Sx6K22h5KKI/AAAAAAAAAuU/DRIi7hO5oFw/s1600-h/Screenshot-Dropbox+-+administrilo+de+dosieroj.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_y8p0_dtMJ38/Sx6K22h5KKI/AAAAAAAAAuU/DRIi7hO5oFw/s320/Screenshot-Dropbox+-+administrilo+de+dosieroj.png" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
На каждой из машин&lt;br /&gt;
1. Удалил существовавшие конфигурационные файлы&lt;br /&gt;
2. Сделал симлинки файлов из Dropbox на соответствующие конфигурационные файлы.&lt;br /&gt;
&lt;br /&gt;
В случае с &lt;a href="http://www.zsh.org/"&gt;ZSH&lt;/a&gt; (впрочем, для любого другого шелла это тоже реализуется) есть ещё один приятный лайфхак - если на разных машинах должны быть немного разные конфиги (например, другая переменная PATH, CDPATH, то это тоже реализуется просто: &lt;br /&gt;
&lt;br /&gt;
Внесите в свой &lt;tt&gt;~/.zshrc&lt;/tt&gt; следующий код.&lt;br /&gt;
&lt;pre class="brush:bash;"&gt;LOCAL_ENVIRONMENT=~/.environment_`hostname`
if [[ -a $LOCAL_ENVIRONMENT ]]
then
    source $LOCAL_ENVIRONMENT
fi

&lt;/pre&gt;&lt;br /&gt;
Теперь нужно создать в домашнем каталоге файл с локальной конфигурацией:&lt;br /&gt;
&lt;pre class="brush:bash;"&gt;touch ~/.environment_`hostname`

&lt;/pre&gt;&lt;br /&gt;
И внести в него конфигурацию, локальную для машины.&lt;br /&gt;
&lt;br /&gt;
Да, этот файл тоже может быть симлинком на файл из dropbox.&lt;br /&gt;
&lt;br /&gt;
P.S. Если вас заинтересовал сервис, то прошу вас регистироваться по &lt;a href="https://www.dropbox.com/referrals/NTI4MDgzMjc5"&gt;реферальной ссылке&lt;/a&gt;, тогда и вам и мне добавят +250 мегабайт бесплатно.&lt;br /&gt;
&lt;br /&gt;
P.P.S. Да, ещё забыл сказать, что сервис достаточно умный и отправляет дельты файлов во время синхронизации. Подробнее - &lt;a href="https://www.dropbox.com/help/8"&gt;в официальной документации&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-1971197807983104412?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=3ryzDE6E77g:ekcigkV82CA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=3ryzDE6E77g:ekcigkV82CA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=3ryzDE6E77g:ekcigkV82CA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=3ryzDE6E77g:ekcigkV82CA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=3ryzDE6E77g:ekcigkV82CA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=3ryzDE6E77g:ekcigkV82CA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=3ryzDE6E77g:ekcigkV82CA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=3ryzDE6E77g:ekcigkV82CA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/3ryzDE6E77g" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/3ryzDE6E77g/dropbox.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_y8p0_dtMJ38/Sx6K22h5KKI/AAAAAAAAAuU/DRIi7hO5oFw/s72-c/Screenshot-Dropbox+-+administrilo+de+dosieroj.png" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/12/dropbox.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-1155013276527510169</guid><pubDate>Mon, 07 Dec 2009 13:25:00 +0000</pubDate><atom:updated>2009-12-07T19:25:49.540+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">rss</category><title>Дайджест ссылок в RSS-ленте</title><description>Решил убрать дайджест ссылок из RSS-ленты.&lt;br /&gt;
&lt;br /&gt;
Те, кому это нужно могут сделать следующее:&lt;br /&gt;
&lt;br /&gt;
1. Добавить в свой RSS-аггрегатор ленту &lt;tt&gt;&lt;a href="http://feeds.delicious.com/v2/rss/dark.schakal?count=15"&gt;http://feeds.delicious.com/v2/rss/dark.schakal?count=15&lt;/a&gt;&lt;/tt&gt;&lt;br /&gt;
2. Если вы используете сервис Delicious, то можете меня добавить меня в свою сеть по ссылке: &lt;a href="http://delicious.com/settings/networkadd?networkadd=dark.schakal"&gt;http://delicious.com/settings/networkadd?networkadd=dark.schakal&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-1155013276527510169?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=9Sd37S6uo-o:HvwWcSi2psc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=9Sd37S6uo-o:HvwWcSi2psc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=9Sd37S6uo-o:HvwWcSi2psc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=9Sd37S6uo-o:HvwWcSi2psc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=9Sd37S6uo-o:HvwWcSi2psc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=9Sd37S6uo-o:HvwWcSi2psc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=9Sd37S6uo-o:HvwWcSi2psc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=9Sd37S6uo-o:HvwWcSi2psc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/9Sd37S6uo-o" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/9Sd37S6uo-o/rss_07.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/12/rss_07.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-8996005673895028731</guid><pubDate>Sat, 05 Dec 2009 19:32:00 +0000</pubDate><atom:updated>2009-12-06T01:32:39.127+06:00</atom:updated><title>RSS лента блога и дайджест ссылок за предыдущий день</title><description>Как вы уже могли заметить, RSS теперь содержит дайджест интересных мне ссылок, которые я нашёл в сети за последнее время.&lt;br /&gt;
&lt;br /&gt;
Отпишитесь в комментариях стоит ли это оставить, или убрать дайджест ссылок из ленты?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-8996005673895028731?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=iMZEQyKNQHI:t-4xN9NPkcU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=iMZEQyKNQHI:t-4xN9NPkcU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=iMZEQyKNQHI:t-4xN9NPkcU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=iMZEQyKNQHI:t-4xN9NPkcU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=iMZEQyKNQHI:t-4xN9NPkcU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=iMZEQyKNQHI:t-4xN9NPkcU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=iMZEQyKNQHI:t-4xN9NPkcU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=iMZEQyKNQHI:t-4xN9NPkcU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/iMZEQyKNQHI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/iMZEQyKNQHI/rss.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>6</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/12/rss.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-8745875516678173355</guid><pubDate>Sat, 05 Dec 2009 19:03:00 +0000</pubDate><atom:updated>2009-12-06T01:12:14.400+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">rabbitvcs</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">svn</category><category domain="http://www.blogger.com/atom/ns#">nautilus</category><title>RabbitVCS - расширение для Nautilus для работы с Subversion</title><description>Раньше я использовал коллекцию скриптов &lt;tt&gt;nautilussvn&lt;/tt&gt;, весьма неудобную в использовании.&lt;br /&gt;
&lt;br /&gt;
Бродя по просторам сети наткнулся на отличную замену. Встречайте - &lt;a href="http://rabbitvcs.org/"&gt;RabbitVCS&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Возможности&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Интеграция с Nautilus&lt;/li&gt;
&lt;li&gt;Поддержка Subversion&lt;/li&gt;
&lt;li&gt;Полная локализация&lt;/li&gt;
&lt;li&gt;Поддержка командной строки&lt;/li&gt;
&lt;li&gt;Доступны пакеты для различных дистрибутивов&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;Установка&lt;/h3&gt;&lt;br /&gt;
&lt;strong&gt;Для Ubuntu Hardy, Intepid, Jaunty&lt;/strong&gt;&lt;br /&gt;
&lt;pre class="brush:bash;"&gt;deb http://ppa.launchpad.net/rabbitvcs/ppa/ubuntu hardy main

&lt;/pre&gt;&lt;br /&gt;
&lt;strong&gt;Для Ubuntu Karmic&lt;/strong&gt;&lt;br /&gt;
&lt;pre class="brush:bash;"&gt;sudo add-apt-repository ppa:rabbitvcs/ppa

&lt;/pre&gt;&lt;br /&gt;
Установка для других дистрибутивов описана &lt;a href="http://wiki.rabbitvcs.org/wiki/download"&gt;на официальном сайте&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Работа&lt;/h3&gt;Несмотря на очень маленький номер версии работает достаточно стабильно, не падает.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Скриншоты&lt;/h4&gt;После установки внешний вид наутилуса преображается и выглядит примерно следующим образом (картинки кликабельны):&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_y8p0_dtMJ38/Sxqtltez5bI/AAAAAAAAAtk/KUPsnbkZTNU/s1600-h/Screenshot-Delicious+-+administrilo+de+dosieroj.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_y8p0_dtMJ38/Sxqtltez5bI/AAAAAAAAAtk/KUPsnbkZTNU/s320/Screenshot-Delicious+-+administrilo+de+dosieroj.png" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
Диалог коммита выглядит вот так:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_y8p0_dtMJ38/Sxqt9y_wAbI/AAAAAAAAAts/YMfhdkKJ_Ck/s1600-h/Screenshot-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_y8p0_dtMJ38/Sxqt9y_wAbI/AAAAAAAAAts/YMfhdkKJ_Ck/s320/Screenshot-1.png" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
Контекстное меню:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_y8p0_dtMJ38/SxqwIz8iOfI/AAAAAAAAAt0/bDLo2ZIzpKY/s1600-h/Screenshot-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_y8p0_dtMJ38/SxqwIz8iOfI/AAAAAAAAAt0/bDLo2ZIzpKY/s320/Screenshot-2.png" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;h3&gt;Заключение&lt;/h3&gt;Достойный аналог TortoiseSVN под линукс.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-8745875516678173355?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=v5_U0B8G5IM:vqpFFxLMQyU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=v5_U0B8G5IM:vqpFFxLMQyU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=v5_U0B8G5IM:vqpFFxLMQyU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=v5_U0B8G5IM:vqpFFxLMQyU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=v5_U0B8G5IM:vqpFFxLMQyU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=v5_U0B8G5IM:vqpFFxLMQyU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=v5_U0B8G5IM:vqpFFxLMQyU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=v5_U0B8G5IM:vqpFFxLMQyU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/v5_U0B8G5IM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/v5_U0B8G5IM/nautilus-subversion.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_y8p0_dtMJ38/Sxqtltez5bI/AAAAAAAAAtk/KUPsnbkZTNU/s72-c/Screenshot-Delicious+-+administrilo+de+dosieroj.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/12/nautilus-subversion.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-2242588892686656946</guid><pubDate>Wed, 25 Nov 2009 17:16:00 +0000</pubDate><atom:updated>2009-11-25T23:40:39.998+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">extensions</category><category domain="http://www.blogger.com/atom/ns#">delicious</category><category domain="http://www.blogger.com/atom/ns#">chromium</category><category domain="http://www.blogger.com/atom/ns#">google chrome</category><title>Расширение Delicious Plugin идёт в массы</title><description>Google запустил сайт &lt;a href="https://chrome.google.com/extensions/" target="_blank"&gt;Chrome Extensions&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
В данный момент сайт доступен только разработчикам расширений. Залил расширение, написал кратенькое описание. Жду запуска сайта. :)&lt;br /&gt;
&lt;br /&gt;
А пока сайт закрыт для рядовых пользователей последнюю версию можно скачать &lt;a href="http://bit.ly/2KYnr8"&gt;тут&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
P.S. А тем временем работа над расширением продолжается.&lt;br /&gt;
&lt;br /&gt;
P.P.S. У расширения появилась своя страничка в сети: &lt;a href="http://code.google.com/p/chromium-delicious-extension/"&gt;Project Home Page&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-2242588892686656946?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=lUf6FV0Jefs:8CD0twju0lo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=lUf6FV0Jefs:8CD0twju0lo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=lUf6FV0Jefs:8CD0twju0lo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=lUf6FV0Jefs:8CD0twju0lo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=lUf6FV0Jefs:8CD0twju0lo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=lUf6FV0Jefs:8CD0twju0lo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=lUf6FV0Jefs:8CD0twju0lo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=lUf6FV0Jefs:8CD0twju0lo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/lUf6FV0Jefs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/lUf6FV0Jefs/delicious-plugin.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>0</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/11/delicious-plugin.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-2444095627869846146</guid><pubDate>Tue, 24 Nov 2009 17:09:00 +0000</pubDate><atom:updated>2009-11-26T00:36:09.128+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">extension</category><category domain="http://www.blogger.com/atom/ns#">chromium</category><title>Создание страницы настроек для расширений Google Chrome</title><description>В продолжение &lt;a href="http://atamanenko.blogspot.com/2009/11/delicious-bookmarks-google.html"&gt;предыдущей заметки&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Логично предположить, что у расширений могут быть настройки. В Google Chrome/Chromium для этого есть специальный API.&lt;br /&gt;
&lt;br /&gt;
Для того, чтобы создать собственную страницу настроек необходимо сделать следующее:&lt;br /&gt;
&lt;br /&gt;
1. Объявить в манифесте страницу настроек&lt;br /&gt;
&lt;pre class="brush:js; highlight: [5]"&gt;{
  "name": "Delicious plugin", 
  "version": "0.2", 
  "background_page": "background.html", 
  "options_page": "options.html"
}
&lt;/pre&gt;&lt;br /&gt;
2. Реализовать страницу с настройками.&lt;br /&gt;
&lt;br /&gt;
Страница с настройками - это обычная HTML-страничка. Для доступа к настройкам Google Chrome предоставляет объект &lt;tt&gt;localStorage&lt;/tt&gt;, который умеет сохранять и возвращать значения.&lt;br /&gt;
&lt;br /&gt;
С объектом &lt;tt&gt;localStorage&lt;/tt&gt; работа идёт как с обычным hash. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:js; html-script: true; highlight:[09, 21, 20, 33, 41]"&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Delicious Bookmarks Options&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
    // Saves options to localStorage.
    function saveOptions() {
        var share = document.getElementById(&amp;quot;share&amp;quot;);
        localStorage[&amp;quot;markPrivate&amp;quot;] = share.checked;

        // Update status to let user know options were saved.
        var status = document.getElementById(&amp;quot;status&amp;quot;);
        status.innerHTML = &amp;quot;Options Saved.&amp;quot;;
        setTimeout(function() {
            status.innerHTML = &amp;quot;&amp;quot;;
        }, 1500);
    }

    // Restores select box state to saved value from localStorage.
    function restoreOptions() {
        var share = localStorage[&amp;quot;markPrivate&amp;quot;];

        if (!share) {
            return;
        }
        var shareCheckbox = document.getElementById(&amp;quot;share&amp;quot;);

        shareCheckbox.checked = share;
        
    }
&amp;lt;/script&amp;gt;

&amp;lt;body onload=&amp;quot;restoreOptions()&amp;quot;&amp;gt;

&amp;lt;label for=&amp;quot;share&amp;quot;&amp;gt;Mark as Private&amp;lt;/label&amp;gt;
&amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;checkbox&amp;quot; name=&amp;quot;share&amp;quot; id=&amp;quot;share&amp;quot; /&amp;gt;


&amp;lt;br&amp;gt;
&amp;lt;button onclick=&amp;quot;saveOptions();&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;
&amp;lt;div id=&amp;quot;status&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;h2&gt;Чтение настроек&lt;/h2&gt;При инициализации страницы настроек необходимо проставить актуальные значения. Для этого на событе &lt;tt&gt;onload&lt;/tt&gt; навешивается функция &lt;tt&gt;restoreOptions()&lt;/tt&gt;, которая проставляет в пользовательском интерфейсе текущие настройки. &lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Запись настроек&lt;/h2&gt;Для сохранения настроек навешивается обработчик &lt;tt&gt;onclick&lt;/tt&gt; для кнопки save - метод &lt;tt&gt;saveOptions&lt;/tt&gt;.&lt;br /&gt;
&lt;br /&gt;
Вот таким простым способом можно сохранять настройки расширения. &lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Документация&lt;/h1&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/options.html"&gt;Google Chrome Extensions Options&lt;/a&gt;&lt;br /&gt;
&lt;li&gt;&lt;a href="http://www.w3.org/TR/2009/WD-webstorage-20091029/#the-localstorage-attribute"&gt;Документация по localStorage&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-2444095627869846146?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4q3JfEDUZOc:rbaXQ0cxWlI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4q3JfEDUZOc:rbaXQ0cxWlI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=4q3JfEDUZOc:rbaXQ0cxWlI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4q3JfEDUZOc:rbaXQ0cxWlI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=4q3JfEDUZOc:rbaXQ0cxWlI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4q3JfEDUZOc:rbaXQ0cxWlI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=4q3JfEDUZOc:rbaXQ0cxWlI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=4q3JfEDUZOc:rbaXQ0cxWlI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/4q3JfEDUZOc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/4q3JfEDUZOc/google-chrome.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>1</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/11/google-chrome.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-5651858402317760145</guid><pubDate>Sat, 14 Nov 2009 16:34:00 +0000</pubDate><atom:updated>2009-11-24T23:07:17.936+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">extension</category><category domain="http://www.blogger.com/atom/ns#">delicious</category><category domain="http://www.blogger.com/atom/ns#">howto</category><category domain="http://www.blogger.com/atom/ns#">chromium</category><category domain="http://www.blogger.com/atom/ns#">article</category><title>Расширение Delicious Bookmarks для Google Chrome/Chromium</title><description>&lt;h1&gt;Введение&lt;/h1&gt;&lt;br /&gt;
Решил научиться писать собственные расширения для &lt;a href="http://www.google.com/chrome"&gt;Google Chrome&lt;/a&gt;/&lt;a href="http://www.chromium.org/"&gt;Chromium&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
За идею взял официальное расширение от &lt;a href="http://m.www.yahoo.com/"&gt;Yahoo!&lt;/a&gt; для &lt;a href="http://www.mozilla.com/en-US/firefox/firefox.html"&gt;Firefox&lt;/a&gt; - &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/3615"&gt;Delicious Bookmarks&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Структура расширения&lt;/h1&gt;&lt;br /&gt;
Расширение - файл с расширением &lt;tt&gt;.crx&lt;/tt&gt;. На самом деле это просто &lt;tt&gt;ZIP&lt;/tt&gt;-архив с файлом манифеста внутри.&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Файл манифеста&lt;/h1&gt;&lt;pre class="brush:js; highlight: [2, 3, 4, 5, 10, 16, 23]"&gt;{
"name": "Delicious plugin", // 1
"version": "0.2", // 2
"background_page": "background.html", // 3
"permissions": [ // 4
"bookmarks",
"tabs"
],

"browser_action": { // 5
"name": "Save bookmark to delicious.com",
"default_title": "Save bookmark to delicious.com",
"default_icon": "delicious.20.gif" // optional
},

"content_scripts": [ // 6
{
"matches": ["http://*/*", "https://*/*"],
"js": ["getDocumentSelection.js"]
}
],

"options_page": "options.html" // 7
}
&lt;/pre&gt;&lt;br /&gt;
Манифест - файл в формате &lt;a href="http://json.org/"&gt;JSON&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Манифест состоит из следующих частей:&lt;br /&gt;
&lt;br /&gt;
1. &lt;span style="font-weight:bold;"&gt;&lt;tt&gt;name&lt;/tt&gt;&lt;/span&gt; - Имя расширения&lt;br /&gt;
2. &lt;span style="font-weight:bold;"&gt;&lt;tt&gt;version&lt;/tt&gt;&lt;/span&gt; - Версия расширения&lt;br /&gt;
3. &lt;span style="font-weight:bold;"&gt;&lt;tt&gt;background_page&lt;/tt&gt;&lt;/span&gt; - основной файл с расширением. Это обычный HTML-файл с разметкой.&lt;br /&gt;
4. &lt;span style="font-weight:bold;"&gt;&lt;tt&gt;permissions&lt;/tt&gt;&lt;/span&gt; - разрешения для расширения. Указываем, что нам нужен доступ к вкладкам (tabs) и закладкам (bookmarks).&lt;br /&gt;
5. &lt;span style="font-weight:bold;"&gt;&lt;tt&gt;browser_action&lt;/tt&gt;&lt;/span&gt; - указываем, что нужно отобразить на панели инструментов браузера.&lt;br /&gt;
6. &lt;span style="font-weight:bold;"&gt;&lt;tt&gt;content_scripts&lt;/tt&gt;&lt;/span&gt; - Для доступа к DOM отображаемой страницы необходимы content scripts, данный блок регистрирует скрипт &lt;tt&gt;getDocumentSelection.js&lt;/tt&gt; для всех &lt;tt&gt;http&lt;/tt&gt; и &lt;tt&gt;https&lt;/tt&gt; страниц.&lt;br /&gt;
7. &lt;span style="font-weight:bold;"&gt;&lt;tt&gt;options_page&lt;/tt&gt;&lt;/span&gt; - страница с настройками.&lt;br /&gt;
&lt;br /&gt;
Подробное описание файла манифеста есть &lt;a href="http://code.google.com/chrome/extensions/manifest.html"&gt;на официальном сайте&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Описание работы расширения&lt;/h1&gt;&lt;br /&gt;
&lt;br /&gt;
Расширение работает следующим образом:&lt;br /&gt;
1. При клике на значок расширения вызывается обработчик &lt;tt&gt;addBookmark&lt;/tt&gt;&lt;br /&gt;
2. Обработчик открывает коммуникационный порт и отправляет сообщение скрипту содержимого.&lt;br /&gt;
3. Скрипт содержимого забирает выделенный текст и отправляет назад расширению выделенный текст.&lt;br /&gt;
4. Расширение забирает выделенный текст, определяет настройки сохранения (Private/Public) и вызывает окно сохранения &lt;span style="font-style:italic;"&gt;Delicious&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Реализация расширения&lt;/h1&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight:bold;"&gt;background.html&lt;/span&gt;:&lt;br /&gt;
&lt;pre class="brush:js; html-script: true"&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;script&amp;nbsp;type=&amp;quot;text/javascript&amp;quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;saveBookmark()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;Send&amp;nbsp;our&amp;nbsp;password&amp;nbsp;to&amp;nbsp;the&amp;nbsp;current&amp;nbsp;tab&amp;nbsp;when&amp;nbsp;clicked.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chrome.tabs.getSelected(null,&amp;nbsp;function(tab)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;port&amp;nbsp;=&amp;nbsp;chrome.tabs.connect(tab.id,&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name&amp;nbsp;:&amp;nbsp;&amp;quot;deliciousBookmark&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;port.postMessage(&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;action&amp;nbsp;:&amp;nbsp;'getSelection'
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;addBookmark(id,&amp;nbsp;bookmark)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(&amp;quot;added&amp;nbsp;bookmark:&amp;nbsp;&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;bookmark);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;saveBookmark();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chrome.bookmarks.onCreated.addListener(addBookmark);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(&amp;quot;Registered&amp;nbsp;listener&amp;quot;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function&amp;nbsp;getShareStatus()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;markPrivate&amp;nbsp;=&amp;nbsp;localStorage[&amp;quot;markPrivate&amp;quot;];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;share&amp;nbsp;=&amp;nbsp;&amp;quot;yes&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(markPrivate&amp;nbsp;==&amp;nbsp;&amp;quot;true&amp;quot;)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;share&amp;nbsp;=&amp;nbsp;&amp;quot;no&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;share;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chrome.extension.onConnect.addListener(function(port)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.assert(port.name&amp;nbsp;==&amp;nbsp;&amp;quot;deliciousBookmark&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;port.onMessage.addListener(function(msg)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;selection&amp;nbsp;=&amp;nbsp;msg.selection;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chrome.tabs.getSelected(null,&amp;nbsp;function(tab)&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;url&amp;nbsp;=&amp;nbsp;encodeURIComponent(tab.url);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(!url&amp;nbsp;||&amp;nbsp;url&amp;nbsp;===&amp;nbsp;&amp;quot;&amp;quot;)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;title&amp;nbsp;=&amp;nbsp;encodeURIComponent(tab.title);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;description&amp;nbsp;=&amp;nbsp;encodeURIComponent(selection);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;share&amp;nbsp;=&amp;nbsp;getShareStatus();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;f&amp;nbsp;=&amp;nbsp;'http://delicious.com/save?url='&amp;nbsp;+&amp;nbsp;url&amp;nbsp;+&amp;nbsp;'&amp;amp;title='&amp;nbsp;+&amp;nbsp;title&amp;nbsp;+&amp;nbsp;'&amp;amp;notes='&amp;nbsp;+&amp;nbsp;description
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;+&amp;nbsp;'&amp;amp;share='&amp;nbsp;+&amp;nbsp;share&amp;nbsp;+&amp;nbsp;'&amp;amp;v=5&amp;amp;';
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window.open(f&amp;nbsp;+&amp;nbsp;'noui=1&amp;amp;jump=doclose',&amp;nbsp;'deliciousuiv5',
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'location=yes,links=no,scrollbars=no,toolbar=no,width=550,height=550');

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chrome.browserAction.onClicked.addListener(saveBookmark);
&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight:bold;"&gt;getDocumentSelection.js&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:js"&gt;var port = chrome.extension.connect( {
name : "deliciousBookmark"
});

// Also listen for new channels from the extension for when the button is
// pressed.
chrome.extension.onConnect.addListener(function(port) {
console.assert(port.name == "deliciousBookmark");
port.onMessage.addListener(function(msg) {
if (msg.action == 'getSelection') {
var responsePort = chrome.extension.connect( {
name : "deliciousBookmark"
});
var description = document.getSelection() ? '' + document.getSelection() : '';
responsePort.postMessage( {
selection : description
});
}
});
});


&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight:bold;"&gt;options.html&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:js; html-script: true"&gt;&amp;lt;html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Delicious Bookmarks Options&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
    // Saves options to localStorage.
    function saveOptions() {
        var share = document.getElementById(&amp;quot;share&amp;quot;);
        localStorage[&amp;quot;markPrivate&amp;quot;] = share.checked;

        // Update status to let user know options were saved.
        var status = document.getElementById(&amp;quot;status&amp;quot;);
        status.innerHTML = &amp;quot;Options Saved.&amp;quot;;
        setTimeout(function() {
            status.innerHTML = &amp;quot;&amp;quot;;
        }, 1500);
    }

    // Restores select box state to saved value from localStorage.
    function restoreOptions() {
        var share = localStorage[&amp;quot;markPrivate&amp;quot;];

        if (!share) {
            return;
        }
        var shareCheckbox = document.getElementById(&amp;quot;share&amp;quot;);

        shareCheckbox.checked = share;
        
    }
&amp;lt;/script&amp;gt;

&amp;lt;body onload=&amp;quot;restoreOptions()&amp;quot;&amp;gt;

&amp;lt;label for=&amp;quot;share&amp;quot;&amp;gt;Mark as Private&amp;lt;/label&amp;gt;
&amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;checkbox&amp;quot; name=&amp;quot;share&amp;quot; id=&amp;quot;share&amp;quot; /&amp;gt;


&amp;lt;br&amp;gt;
&amp;lt;button onclick=&amp;quot;saveOptions();&amp;quot;&amp;gt;Save&amp;lt;/button&amp;gt;
&amp;lt;div id=&amp;quot;status&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Сборка расширения&lt;/h1&gt;&lt;br /&gt;
В Chromium есть возможность собрать расширение.&lt;br /&gt;
1. Открываем адрес &lt;tt&gt;chrome://extensions/&lt;/tt&gt;&lt;br /&gt;
2. В правом верхнем углу нажимаем Developer Mode.&lt;br /&gt;
3. На появившейся панели инструментов кликаем Pack Extension&lt;br /&gt;
4. Выбираем каталог, в котором находится расширение&lt;br /&gt;
5. Chromium автоматически собирает расширение в каталоге с исходниками расширения.&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Заключение&lt;/h1&gt;&lt;br /&gt;
Писать расширения для Google Chrome не просто, а очень просто. &lt;br /&gt;
&lt;br /&gt;
Готовое и собранное расширение можно &lt;a href="http://bit.ly/2KYnr8"&gt;скачать здесь&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Дополнительная информация&lt;/h1&gt;&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/devguide.html"&gt;Google Chrome Extensions Developer's Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/manifest.html"&gt;Manifest file description&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/api_index.html"&gt;Chrome Extensions API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/chrome/extensions/packaging.html"&gt;Extension Packaging&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;br /&gt;
В следующей заметке опишу работу messaging для коммуникации расширения со скриптами содержимого.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-5651858402317760145?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=SvwGt8RGqec:CFoBFQNDNNc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=SvwGt8RGqec:CFoBFQNDNNc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=SvwGt8RGqec:CFoBFQNDNNc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=SvwGt8RGqec:CFoBFQNDNNc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=SvwGt8RGqec:CFoBFQNDNNc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=SvwGt8RGqec:CFoBFQNDNNc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=SvwGt8RGqec:CFoBFQNDNNc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=SvwGt8RGqec:CFoBFQNDNNc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/SvwGt8RGqec" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/SvwGt8RGqec/delicious-bookmarks-google.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>9</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/11/delicious-bookmarks-google.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-8421063008720169663</guid><pubDate>Mon, 02 Nov 2009 17:26:00 +0000</pubDate><atom:updated>2009-11-19T15:12:45.988+06:00</atom:updated><title>Инвайты на Google Wave</title><description>Кому нужны инвайты на Google Wave, отписываемся в комментариях - поделюсь.

&lt;span style="font-weight:bold;"&gt;UPD&lt;/span&gt;. Инвайты закончились, всем спасибо.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-8421063008720169663?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=FUtoxKUhDW0:4GilHHDmrwU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=FUtoxKUhDW0:4GilHHDmrwU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=FUtoxKUhDW0:4GilHHDmrwU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=FUtoxKUhDW0:4GilHHDmrwU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=FUtoxKUhDW0:4GilHHDmrwU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=FUtoxKUhDW0:4GilHHDmrwU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=FUtoxKUhDW0:4GilHHDmrwU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=FUtoxKUhDW0:4GilHHDmrwU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/FUtoxKUhDW0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/FUtoxKUhDW0/google-wave.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>12</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/11/google-wave.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-2039733364952658376</guid><pubDate>Tue, 07 Jul 2009 15:11:00 +0000</pubDate><atom:updated>2009-12-15T08:12:20.263+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">foreign key</category><category domain="http://www.blogger.com/atom/ns#">constraint</category><category domain="http://www.blogger.com/atom/ns#">mysql</category><category domain="http://www.blogger.com/atom/ns#">sybase</category><category domain="http://www.blogger.com/atom/ns#">база данных</category><category domain="http://www.blogger.com/atom/ns#">transaction</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><category domain="http://www.blogger.com/atom/ns#">postgresql</category><category domain="http://www.blogger.com/atom/ns#">oracle</category><title>Двунаправленная ссылочная целостность</title><description>Здравствуйте.&lt;br /&gt;
&lt;br /&gt;
Я считаю, что разработчику всегда стоит иметь представление о том, как работают низлежащие абстракции. Хотя бы потому, что часто &lt;a href="http://c2.com/cgi/wiki?LeakyAbstraction"&gt;абстракции&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;бывают&lt;/a&gt; &lt;a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html"&gt;протекающими&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
А веду я это к тому, что когда фреймворк вываливает стектрейс с несколькими вложенными (и, часто, для непосвященных непонятными) исключениями, то бывает непонятно, что с этим делать, особенно, если не знаешь, как реализована абстракция.&lt;br /&gt;
&lt;br /&gt;
Иногда бывает нужно хранить двунаправленные связи между таблицами. Например, есть игрок и есть его статистика и нужно, чтобы каждая из сущностей знала про другую.&lt;br /&gt;
&lt;br /&gt;
Вопрос - каким образом вставлять в базу данных такие сущности?&lt;br /&gt;
&lt;br /&gt;
При вставке в таблицу в Player нам нужно уже иметь запись в таблице Statistics, а для вставки в таблицу Statistics нужно знать идентификатор игрока, к которому относится эта запись.&lt;br /&gt;
&lt;br /&gt;
Выглядит замкнутым кругом, но решение есть.&lt;br /&gt;
&lt;p&gt;Во-первых, вставки в обе таблицы должны происходить в рамках одной транзакции (как я &lt;a href="http://www.blogger.com/2009/04/blog-post_24.html"&gt;уже писал&lt;/a&gt;, транзакция обеспечивает перевод базы данных из одного непротиворечащего состояния в другое непротиворечащее состояние).&lt;br /&gt;
&lt;/p&gt;&lt;br /&gt;
К сожалению, этого недостаточно:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: sql"&gt;begin transaction;

INSERT INTO "Player"  VALUES (10, 20);
INSERT INTO "Statistics"  VALUES (20, 10);

commit;
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
выдаёт следующую ошибку:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush: plain"&gt;ERROR: insert or update on table "Player" violates foreign key constraint "statisticsId"
SQL state: 23503
Detail: Key (statisticsId)=(20) is not present in table "Statistics".
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
И снова кажется, что замкнутый круг не разорвать.&lt;br /&gt;
&lt;br /&gt;
Но решение всё же есть.&lt;br /&gt;
Многие базы данных позволяют нарушать консистентность базы данных внутри транзакции. В данном случае можно "попросить" базу данных отложить проверку ссылочной целостности. На примере PostgreSQL рассмотрим, как это работает.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;PostgreSQL&lt;/h3&gt;&lt;br /&gt;
&lt;pre class="brush: sql"&gt;begin transaction;

set constraints all deferred; -- 1

INSERT INTO "Player"  VALUES (10, 20);
INSERT INTO "Statistics"  VALUES (20, 10);

set constraints all immediate; -- 2

commit; -- 3
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
1 - Сделать все ограничения отложенными. Ограничения будут проверены в момент проведения фиксации транзакции (в строке 3)&lt;br /&gt;
2 - Явно делаем все ограничения немедленными. В данном примере это необязательно, так как в 3) они всё равно будут проверены.&lt;br /&gt;
&lt;br /&gt;
После выполнения данного кода получаем:&lt;br /&gt;
&lt;pre class="brush: plain"&gt;Query returned successfully with no result in 24 ms.
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
Это уже полностью рабочий пример, чтобы это заработало нужно явно сделать ограничения отложенными - они должны быть объявлены как DEFERRED. Официальная документация к PostgreSQL гласит:&lt;br /&gt;
&lt;blockquote&gt;Upon creation, a constraint is given one of three characteristics: DEFERRABLE INITIALLY DEFERRED, DEFERRABLE INITIALLY IMMEDIATE, or NOT DEFERRABLE. The third class is always IMMEDIATE and is not affected by the SET CONSTRAINTS command. The first two classes start every transaction in the indicated mode, but their behavior can be changed within a transaction by SET CONSTRAINTS.&lt;/blockquote&gt;&lt;br /&gt;
&lt;br /&gt;
Так что необходимо также модифицировать объявления ограничений:&lt;br /&gt;
&lt;pre class="brush: sql"&gt;ALTER TABLE "Player"
ADD CONSTRAINT "statisticsId" FOREIGN KEY ("statisticsId")
REFERENCES "Statistics" ("statisticsId") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY IMMEDIATE;

&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
Данный пример был приведён для PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
Для других баз данных решения следующие:&lt;br /&gt;
&lt;h3&gt;Sybase&lt;/h3&gt;&lt;br /&gt;
В Sybase есть переменная &lt;a href="http://manuals.sybase.com/onlinebooks/group-sasarc/awg0600e/dbugen6/@Generic__BookTextView/26075;hf=0;pt=25811"&gt;WAIT_FOR_COMMIT&lt;/a&gt;, которая управляет поведением проверки ограничений. По умолчанию она отключена. Кроме того, это поведение можно переопределить при объявлении можно указывать CHECK ON COMMIT. Подробнее можно посмотреть в &lt;a href="http://manuals.sybase.com/onlinebooks/group-pbarc/conn5/sqlug/@Generic__BookTextView/39397%3Bpt=39951"&gt;официальной документации&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Oracle&lt;/h3&gt;&lt;br /&gt;
Также, как и в PostgreSQL:&lt;br /&gt;
&lt;blockquote&gt;You can define constraints as either deferrable or not deferrable, and either initially deferred or initially immediate. These attributes can be different for each constraint. You specify them with keywords in the CONSTRAINT clause:&lt;br /&gt;
DEFERRABLE or NOT DEFERRABLE&lt;br /&gt;
INITIALLY DEFERRED or INITIALLY IMMEDIATE&lt;br /&gt;
&lt;br /&gt;
Constraints can be added, dropped, enabled, disabled, or validated. You can also modify a constraint's attributes.&lt;br /&gt;
&lt;/blockquote&gt;&lt;br /&gt;
Подробнее в &lt;a href="http://download.oracle.com/docs/cd/B10501_01/server.920/a96524/c22integ.htm#4666"&gt;официльной документации&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;mySQL&lt;/h3&gt;&lt;br /&gt;
К сожалению, mySQL не имеет возможности отложенной проверки ограничении, о чём в документации и сказано:&lt;br /&gt;
&lt;blockquote&gt;&lt;em&gt;Deviation from SQL standards&lt;/em&gt;: Like MySQL in general, in an SQL statement that inserts, deletes, or updates many rows, InnoDB checks UNIQUE and FOREIGN KEY constraints row-by-row. According to the SQL standard, the default behavior should be deferred checking. That is, constraints are only checked after the entire SQL statement has been processed. Until InnoDB implements deferred constraint checking, some things will be impossible, such as deleting a record that refers to itself via a foreign key.&lt;/blockquote&gt;&lt;br /&gt;
&lt;br /&gt;
Но это не конец света, так как можно отключить проверку внешних ключей:&lt;br /&gt;
&lt;pre class="brush: sql"&gt;SET foreign_key_checks = 0;
-- code goes here
SET foreign_key_checks = 1;
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
Чем эта информация может быть полезна для разработчика? Например, в случае, если ошибки и недоработки Hibernate покажут себя в виде стектрейса в логе(например, &lt;a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-2248"&gt;HHH-2248&lt;/a&gt;), то разработчик будет понимать причину ошибки и то, как реализовать work-around.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-2039733364952658376?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2iHSZJn5ABc:CKSKWi2pkHk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2iHSZJn5ABc:CKSKWi2pkHk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=2iHSZJn5ABc:CKSKWi2pkHk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2iHSZJn5ABc:CKSKWi2pkHk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=2iHSZJn5ABc:CKSKWi2pkHk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2iHSZJn5ABc:CKSKWi2pkHk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=2iHSZJn5ABc:CKSKWi2pkHk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=2iHSZJn5ABc:CKSKWi2pkHk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/2iHSZJn5ABc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/2iHSZJn5ABc/blog-post.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>1</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/07/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-4488864395166590348</guid><pubDate>Fri, 03 Jul 2009 18:13:00 +0000</pubDate><atom:updated>2009-11-22T02:10:31.327+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">delicious</category><category domain="http://www.blogger.com/atom/ns#">opera</category><title>Opera и del.icio.us</title><description>Так получилось, что в последнее время я стал пользоваться небезызвестной &lt;a href="http://www.opera.com"&gt;оперой&lt;/a&gt;.

В связи с этим я стал искать замену часто используемым расширениям, одно из них - delicious bookmarks. Сервис &lt;a href="http://delicious.com"&gt;del.icio.us&lt;/a&gt; предоставляет букмарклеты для различных браузеров, которые позволяют сохранять закладки быстро и удобно. Вот только есть один недостаток - &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/3615"&gt;официальный плагин для Firefox&lt;/a&gt; в свойство Notes новой закладки добавляет выделенный текст со страницы, чего букмарклет не делает.

Посмотрев исходник букмарклета, а это обычный javascript, я добавил небольшой кусочек кода, который восстанавливает справедливость и тоже добавляет в поле Notes текст, выделенный на странице.

Ниже привожу изменённый код букмарклета:
&lt;pre class="brush:js"&gt;
javascript:(function(){f='http://delicious.com/save?url='+encodeURIComponent(window.location.href)+'&amp;title='+encodeURIComponent(document.title)+'&amp;notes='+encodeURIComponent(document.getSelection())+'&amp;v=5&amp;';a=function(){if(!window.open(f+'noui=1&amp;jump=doclose','deliciousuiv5','location=yes,links=no,scrollbars=no,toolbar=no,width=550,height=550'))location.href=f+'jump=yes'};if(/Firefox/.test(navigator.userAgent)){setTimeout(a,0)}else{a()}})()
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-4488864395166590348?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=ddvRyQ-WoAk:hBsTOVkUmQA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=ddvRyQ-WoAk:hBsTOVkUmQA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=ddvRyQ-WoAk:hBsTOVkUmQA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=ddvRyQ-WoAk:hBsTOVkUmQA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=ddvRyQ-WoAk:hBsTOVkUmQA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=ddvRyQ-WoAk:hBsTOVkUmQA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=ddvRyQ-WoAk:hBsTOVkUmQA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=ddvRyQ-WoAk:hBsTOVkUmQA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/ddvRyQ-WoAk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/ddvRyQ-WoAk/opera-delicious.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>2</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/07/opera-delicious.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-8695534527351413761</guid><pubDate>Sat, 06 Jun 2009 15:19:00 +0000</pubDate><atom:updated>2009-11-22T00:51:29.232+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">java</category><category domain="http://www.blogger.com/atom/ns#">spring roo</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><title>Spring Roo (часть 2)</title><description>&lt;h1&gt;Архитектура сгенерированного приложения&lt;/h1&gt;
Spring Roo активно использует аспекты. В качестве реализации аспектов была взята библиотека &lt;a href="http://www.eclipse.org/aspectj/"&gt;aspectj&lt;/a&gt;.
Большая часть сгенерированного кода попадает в отдельные файлы-аспекты.
Создадим простой класс:
&lt;pre class="brush: java"&gt;
new persistent class jpa -name ~.domain.Action -testAutomatically
add field string name -notNull -sizeMin 1 -sizeMax 80
add field string description -sizeMax 1024
&lt;/pre&gt;
Spring Roo создаст нам следующие файлы:

&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Action.java&lt;/span&gt;
Класс содержит только полезную с точки зрения назначения класса информацию. По сути, то, что вводит разработчик в консоль Roo аккумулируется здесь. Весь дополнительный код разносится по другим классам.
&lt;pre class="brush: java"&gt;
@Entity //1
@RooJavaBean //2
@RooToString //3
@RooEntity(finders = { "findActionsByName" }) //4
public class Action {

@NotNull
@Size(min = 1, max = 80)
private String name;


@Size(max = 1024)
private String description;
}
&lt;/pre&gt;
1) Класс является JPA-сущностью.
2) Аннотация @RooJavaBean говорит, что этот класс - обычный Java-бин.
3) Аннотация @RooToString говорит, что у этого класса есть перегруженный метод toString().
4) Аннотация @RooEntity говорит что класс является JPA-сущностью.
&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Action_Roo_Configurable.aj&lt;/span&gt;
Данный аспект говорит, что класс Action является конфигурируемым через Spring.
&lt;pre class="brush: java"&gt;
privileged aspect Action_Roo_Configurable {
declare @type: Action: @org.springframework.beans.factory.annotation.Configurable;
}
&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Action_Roo_Entity.aj&lt;/span&gt;
В данном классе собраны все методы DAO для работы с классом Action.
&lt;pre class="brush: java"&gt;
package org.academ.uthark.research.roo.domain;

privileged aspect Action_Roo_Entity {

@javax.persistence.PersistenceContext
transient javax.persistence.EntityManager Action.entityManager;

@javax.persistence.Id
@javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
@javax.persistence.Column(name = "id")
private java.lang.Long Action.id;

@javax.persistence.Version
@javax.persistence.Column(name = "version")
private java.lang.Integer Action.version;

public java.lang.Long Action.getId() {
return this.id;
}

public void Action.setId(java.lang.Long id) {
this.id = id;
}

public java.lang.Integer Action.getVersion() {
return this.version;
}

public void Action.setVersion(java.lang.Integer version) {
this.version = version;
}

@org.springframework.transaction.annotation.Transactional
public void Action.persist() {
if (this.entityManager == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
this.entityManager.persist(this);
}

@org.springframework.transaction.annotation.Transactional
public void Action.remove() {
if (this.entityManager == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
this.entityManager.remove(this);
}

@org.springframework.transaction.annotation.Transactional
public void Action.flush() {
if (this.entityManager == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
this.entityManager.flush();
}

@org.springframework.transaction.annotation.Transactional
public void Action.merge() {
if (this.entityManager == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
Action merged = this.entityManager.merge(this);
this.entityManager.flush();
this.id = merged.getId();
}

public static long Action.countActions() {
return (Long) new Action().entityManager.createQuery("select count(o) from Action o").getSingleResult();
}

public static java.util.List&lt;org.academ.uthark.research.roo.domain.action&gt; Action.findAllActions() {
return new Action().entityManager.createQuery("select o from Action o").getResultList();
}

public static org.academ.uthark.research.roo.domain.Action Action.findAction(java.lang.Long id) {
if (id == null) throw new IllegalArgumentException("An identifier is required to retrieve an instance of Action");
return new Action().entityManager.find(Action.class, id);
}

public static java.util.List&lt;org.academ.uthark.research.roo.domain.action&gt; Action.findActionEntries(int firstResult, int maxResults) {
return new Action().entityManager.createQuery("select o from Action o").setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
}

}
&lt;/org.academ.uthark.research.roo.domain.action&gt;&lt;/org.academ.uthark.research.roo.domain.action&gt;&lt;/pre&gt;
Как легко видеть, этот аспект добавляет классу Action CRUD-методы, поля id и version.
&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Action_Roo_Finder.aj&lt;/span&gt;
Этот аспект аккумулирует все finder-методы, созданные через консоль Spring Roo.
&lt;pre class="brush: java"&gt;privileged aspect Action_Roo_Finder {

public static javax.persistence.Query Action.findActionsByName(java.lang.String name) {
if (name == null) throw new IllegalArgumentException("The name argument is required");
javax.persistence.Query q = new Action().entityManager.createQuery("FROM Action AS action WHERE action.name = :name");
q.setParameter("name", name);
return q;
}

}
&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Action_Roo_JavaBean.aj&lt;/span&gt;
Для уменьшения загромождения кода ненужными методами для доступа к полям Spring Roo выносит их в отдельный аспект.
&lt;pre class="brush: java"&gt;
privileged aspect Action_Roo_JavaBean {

public java.lang.String Action.getName() {
return this.name;
}

public void Action.setName(java.lang.String name) {
this.name = name;
}

public java.lang.String Action.getDescription() {
return this.description;
}

public void Action.setDescription(java.lang.String description) {
this.description = description;
}

}
&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Action_Roo_Plural.aj
В данном аспекте можно определить, как созданная сущность выглядит во множественном числе.
&lt;pre class="brush: java"&gt;
package org.academ.uthark.research.roo.domain;

privileged aspect Action_Roo_JavaBean {

public java.lang.String Action.getName() {
return this.name;
}

public void Action.setName(java.lang.String name) {
this.name = name;
}

public java.lang.String Action.getDescription() {
return this.description;
}

public void Action.setDescription(java.lang.String description) {
this.description = description;
}

}

&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Action_Roo_ToString.aj&lt;/span&gt;
Аспект, переопределяющий toString()
&lt;pre class="brush: java"&gt;
privileged aspect Action_Roo_ToString {

public java.lang.String Action.toString() {
StringBuilder sb = new StringBuilder();
sb.append("id: ").append(getId()).append(", ");
sb.append("version: ").append(getVersion()).append(", ");
sb.append("name: ").append(getName()).append(", ");
sb.append("description: ").append(getDescription()).append(", ");
return sb.toString();
}

}

&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;ActionEditor.java&lt;/span&gt;
Кроме того, при создании контроллеров для работы дополнительно создается ActionEditor
&lt;pre class="brush: java"&gt;
import org.springframework.roo.addon.property.editor.RooEditor;

@RooEditor(providePropertyEditorFor = Action.class)
public class ActionEditor {
}

&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;ActionEditor_Roo_Editor.aj&lt;/span&gt;
Аспект с реализацией.
&lt;pre class="brush: java"&gt;
privileged aspect ActionEditor_Roo_Editor {

declare parents: ActionEditor implements java.beans.PropertyEditorSupport;

org.springframework.beans.SimpleTypeConverter ActionEditor.typeConverter = new org.springframework.beans.SimpleTypeConverter();

public java.lang.String ActionEditor.getAsText() {
 Object obj = getValue(); 
 if (obj == null) { 
     return null;     
 } 
 return (String) typeConverter.convertIfNecessary(((org.academ.uthark.research.roo.domain.Action) obj).getId() , String.class); 
}

public void ActionEditor.setAsText(java.lang.String text) {
 if (text == null || "".equals(text)) { 
     setValue(null);     
     return;     
 } 

 java.lang.Long identifier = (java.lang.Long) typeConverter.convertIfNecessary(text, java.lang.Long.class); 
 if (identifier == null) { 
     setValue(null);     
     return;     
 } 

 setValue(org.academ.uthark.research.roo.domain.Action.findAction(identifier)); 
}

}

&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h3&gt;А где же equals()/hashCode()?&lt;/h3&gt;
Легко заметить, что Spring Roo забыл ещё про один важный аспект - про реализацию методов equals() и hashCode(). К релизу, я думаю, эта недоработка будет исправлена.

Как видно, код скрипта для генерации весьма и весьма лаконичен, а получающий java-код наглядно показывает всю многословность языка Java.

&lt;h3&gt;Apache Maven integration&lt;/h3&gt;
Spring Roo при создании проекта создаёт pom.xml, хорошо знакомый всем работавшим с maven. Кроме того, консоль позволяет добавлять и удалять зависимости, не правя файл вручную.

&lt;h3&gt;Расширения&lt;/h3&gt;
Spring Roo поддерживает расширения, что ещё больше даёт возможностей. На сегодняшний день написано уже как минимум одно расширение - &lt;a href="http://jira.springframework.org/browse/ROO-41"&gt;Sitemesh Addon&lt;/a&gt;.

&lt;h3&gt;Критика&lt;/h3&gt;
В первую очередь критика относится к текущему релизу, а так как он ещё в глубокой альфе, то все может измениться к официальному релизу.
&lt;ol&gt;&lt;li&gt;Нет возможности подставлять свои JSP-шаблоны для сгенерированного CRUD&lt;/li&gt;&lt;li&gt;В сгенерированном коде есть ошибки, например, если заводим поле типа флоат, создаём контроллер, то при редактировании сущности возможна следующая ошибка: вводим достаточно большое число (1234567), сохраняем сущность, открываем на редактирование. В поле, хранящем флоат получаем 1.23456E7, и после этого невозможно сохранить сущность, так как получаем ошибку валидации.&lt;/li&gt;&lt;li&gt;Ещё одна ошибка связана с отношением один ко многим. Создаём пару сущностей с таким отношением. Запускаем, пробуем создать сущность. Roo умничает, если нет связанных сущностей в БД, то он даже не генерирует &amp;lt;select&amp;gt;
&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Вывод&lt;/h3&gt;В целом, Spring Roo достойное начинание, но пользоваться им в Production можно будет ещё нескоро.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-8695534527351413761?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mkfLHYBPxK0:X6XChVIPw9g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mkfLHYBPxK0:X6XChVIPw9g:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=mkfLHYBPxK0:X6XChVIPw9g:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mkfLHYBPxK0:X6XChVIPw9g:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=mkfLHYBPxK0:X6XChVIPw9g:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mkfLHYBPxK0:X6XChVIPw9g:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=mkfLHYBPxK0:X6XChVIPw9g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=mkfLHYBPxK0:X6XChVIPw9g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/mkfLHYBPxK0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/mkfLHYBPxK0/spring-roo-2.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>1</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/06/spring-roo-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8483146613266894707.post-8927437857965741024</guid><pubDate>Tue, 02 Jun 2009 17:06:00 +0000</pubDate><atom:updated>2009-11-21T22:41:05.717+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">spring roo</category><category domain="http://www.blogger.com/atom/ns#">разработка</category><title>Введение в Spring Roo</title><description>&lt;a href="http://blog.springsource.com/2009/05/01/roo-part-1/"&gt;Недавно&lt;/a&gt; компания &lt;a href="http://springsource.com/"&gt;Spring Source&lt;/a&gt; презентовала новый продукт - &lt;a href="http://www.springsource.org/roo"&gt;Spring Roo&lt;/a&gt;. &lt;a href="http://jira.springframework.org/browse/ROO-1"&gt;Цель проекта&lt;/a&gt; - повысить продуктивность Java-разработчиков. &lt;a href="http://stsmedia.net/introducing-spring-roo/"&gt;Почитав&lt;/a&gt; &lt;a href="http://stsmedia.net/introducing-spring-roo-part-2-security-jms-email-support/"&gt;обзоры&lt;/a&gt;, а также &lt;a href="http://blog.springsource.com/2009/05/27/roo-part-2/"&gt;потрогав его руками&lt;/a&gt; можно сделать вывод о том, что это, в некотором виде, альтернатива &lt;a href="http://appfuse.org/"&gt;AppFuse&lt;/a&gt; и &lt;a href="http://grails.codehaus.org/"&gt;Grails&lt;/a&gt;.

&lt;h2&gt;Описание Spring Roo&lt;/h2&gt;&lt;li&gt;100% программирование на Java, предлагающее разработчикам известную, развитую и популярную платформу разработки.&lt;/li&gt;&lt;li&gt;Прозрачные, надёжные и продуктивные сервисы среды разработки, такие как помощник кода, отладчики, визуальные отчёты об ошибка и т.д.&lt;/li&gt;&lt;li&gt;Экстремально эффективная производительность во время выполнения, безопасность типов и отсутствие зависимостей на Roo во время выполнения.&lt;/li&gt;&lt;li&gt;Автоматическая архитектура с использование лучших практик Spring Framework 3
&lt;/li&gt;&lt;li&gt;Структура проекта основана на &lt;a href="http://maven.apache.org/"&gt;Maven2&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Работа с БД основана на &lt;a href="http://java.sun.com/javaee/technologies/entapps/persistence.jsp"&gt;JPA&lt;/a&gt; (например, через &lt;a href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt;), 100% совместимость с JPA и переносимость реализации.&lt;/li&gt;&lt;li&gt;Встроенная поддержка конфигурирования баз данных, с автоматической настройкой JDBC для большинства популярных баз данных.&lt;/li&gt;&lt;li&gt;Прозрачная поддержка внедрения зависимостей (dependency injection) и методов сохранения для всех сущностей, включая полученные через JPA.&lt;/li&gt;&lt;li&gt;Поддержка валидации бинов (&lt;a href="http://jcp.org/en/jsr/detail?id=303"&gt;JSR 303&lt;/a&gt;), включая распространение ограничений вплость до DDL базы данных.&lt;/li&gt;&lt;li&gt;Автоматические интеграционные тесты на &lt;a href="http://junit.org"&gt;JUnit&lt;/a&gt;, которые создаются на базе возможностей интеграционного тестирования Spring Framework.&lt;/li&gt;&lt;li&gt;Автоматическая поддержка REST на уровне бэкэнда.&lt;/li&gt;&lt;li&gt;Автоматизированные тесты для веб-слоя с использованием &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Динамическое создание finder-методов на JPA QL для сущностей безо всякого кодирования.&lt;/li&gt;&lt;li&gt;Интеграция &lt;a href="http://static.springsource.org/spring-security/site/"&gt;Spring Security&lt;/a&gt;, включая безопасность адресов с установкой в одну строку ("install security")
&lt;/li&gt;&lt;li&gt;Поддеркжа &lt;a href="http://www.springsource.org/webflow"&gt;Spring Web Flow&lt;/a&gt; одной командой ("install web flow")&lt;/li&gt;&lt;li&gt;Поддержка мгновенной отправки почтовых сообщение - даже через удалённые SMTP сервера, как GMail.&lt;/li&gt;&lt;li&gt;Встроенная поддержка конфигурирования &lt;a href="http://logging.apache.org/log4j"&gt;log4j&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Использование URL rewriting которое позволяет оставаться ссылкам чистыми и поддерживающими REST.
&lt;/li&gt;&lt;li&gt;Лёгкое ручное создание веб-контроллеров&lt;/li&gt;&lt;li&gt;Поддержка полного цикла жизни приложения с сохранением высокой продуктивности.&lt;/li&gt;&lt;li&gt;Использование встроенного сервлет контейнера &lt;a href="http://tomcat.apache.org"&gt;Tomcat&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Полная интеграция с &lt;a href="http://eclipse.org"&gt;Eclipse&lt;/a&gt; и &lt;a href="http://www.springsource.com/products/sts"&gt;SpringSource Tool Suite&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Лёгкая в использовании, с поддержка автодополнения, подсказок и контекста командная строка&lt;/li&gt;&lt;li&gt;Поддержка автоматизации через скрипты&lt;/li&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8483146613266894707-8927437857965741024?l=atamanenko.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=AL9k3did_Ik:6N5Ju-RB8Y8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=AL9k3did_Ik:6N5Ju-RB8Y8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=AL9k3did_Ik:6N5Ju-RB8Y8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=AL9k3did_Ik:6N5Ju-RB8Y8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=AL9k3did_Ik:6N5Ju-RB8Y8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=AL9k3did_Ik:6N5Ju-RB8Y8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/atamanenko?a=AL9k3did_Ik:6N5Ju-RB8Y8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/atamanenko?i=AL9k3did_Ik:6N5Ju-RB8Y8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/atamanenko/~4/AL9k3did_Ik" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/atamanenko/~3/AL9k3did_Ik/spring-roo.html</link><author>noreply@blogger.com (Oleg Atamanenko)</author><thr:total>3</thr:total><feedburner:origLink>http://atamanenko.blogspot.com/2009/06/spring-roo.html</feedburner:origLink></item></channel></rss>

