14 December, 2009

Настройка autofs для монтирования NFS-ресурсов

Summary: В данной заметке описана настройка autofs для доступа к сетевым ресурсам, доступным по NFS.

Последние несколько лет (с тех пор, как количество компьютеров дома стало больше одного) возникла проблема беспрепятственного доступа к данным, хранящимся на одном компьютере с другого.

Было найдено самое простое решение - NFS.

Как выяснилось позже это было не самое удачное решение - ноутбук не всегда находился дома, а, значит, домашние сетевые ресурсы не всегда доступны. Соответственно, при загрузке операционной системы происходили задержки из-за поиска компьютера с сетевыми ресурсами. Не очень удобно, но жить можно.

Решил окончательно разобраться с этим и начал искать решение. Оно оказалось на поверхности и затронуло только клиента ресурсов, то есть ноутбук. Решение называется autofs.

1. Устанавливаем autofs.
sudo aptitude install autofs5 nfs-common

2. Производим настройку.
Редактируем файл /etc/auto.master. Расскомментируем строку, содержащую строки /net -hosts:
/net -hosts
+auto.master

3. В файл /etc/hosts можно внести адреса серверов с NFS-ресурсами (для того, чтобы избежать DNS-запросов). В моём случае:
192.168.18.1 server

4. Перезапускаем сервис autofs
sudo service autofs restart

5. Теперь открываем в файловом браузер адрес /net/server и видим его сетевые ресурсы, доступные для данного клиента.



6. Пользуемся.

08 December, 2009

Расширение для работы с Delicious доступно на сайте расширений Google Chrome

Итак, расширение для работы с Delicious доступно на сайте расширений Google Chrome.

Посмотреть и установить

Новые версии расширения буду закачить на этот сайт.

Использование Dropbox для хранения конфигурационных файлов

Есть такой замечательный сервис - Dropbox.

Этот сервис является онлайн-хранилищем ваших данных. Работает очень просто:

1. Вы регистрируетесь в сервисе.
2. Устанавливаете клиент
3. Пользуетесь.

Недавно я понял, что этот сервис можно легко и непринуждённо использовать не только для хранения различных документов и фотографий, а также и конфигурационных файлов приложений, что существенно упрощает жизнь, когда у вас несколько рабочих машин (например, у меня домашний компьютер, ноутбук, а также ещё и рабочий) - необходимо каким-то образом синхронизовать конфиги при изменении их на одной из машине с другими.

Итак, что я сделал.

Нижеприведённые действия я делал на одной машине:
1. Создал каталог ConfigFiles
2. Переместил в него свои конфигурационные файлы (~/.zshrc, ~/.zsh, ~/.vimrc, ~/.vim)
3. Из каталога Dropbox сделал соответствующие симлинки в домашнем каталоге.



На каждой из машин
1. Удалил существовавшие конфигурационные файлы
2. Сделал симлинки файлов из Dropbox на соответствующие конфигурационные файлы.

В случае с ZSH (впрочем, для любого другого шелла это тоже реализуется) есть ещё один приятный лайфхак - если на разных машинах должны быть немного разные конфиги (например, другая переменная PATH, CDPATH, то это тоже реализуется просто:

Внесите в свой ~/.zshrc следующий код.
LOCAL_ENVIRONMENT=~/.environment_`hostname`
if [[ -a $LOCAL_ENVIRONMENT ]]
then
    source $LOCAL_ENVIRONMENT
fi


Теперь нужно создать в домашнем каталоге файл с локальной конфигурацией:
touch ~/.environment_`hostname`


И внести в него конфигурацию, локальную для машины.

Да, этот файл тоже может быть симлинком на файл из dropbox.

P.S. Если вас заинтересовал сервис, то прошу вас регистироваться по реферальной ссылке, тогда и вам и мне добавят +250 мегабайт бесплатно.

P.P.S. Да, ещё забыл сказать, что сервис достаточно умный и отправляет дельты файлов во время синхронизации. Подробнее - в официальной документации.

07 December, 2009

Дайджест ссылок в RSS-ленте

Решил убрать дайджест ссылок из RSS-ленты.

Те, кому это нужно могут сделать следующее:

1. Добавить в свой RSS-аггрегатор ленту http://feeds.delicious.com/v2/rss/dark.schakal?count=15
2. Если вы используете сервис Delicious, то можете меня добавить меня в свою сеть по ссылке: http://delicious.com/settings/networkadd?networkadd=dark.schakal

05 December, 2009

RSS лента блога и дайджест ссылок за предыдущий день

Как вы уже могли заметить, RSS теперь содержит дайджест интересных мне ссылок, которые я нашёл в сети за последнее время.

Отпишитесь в комментариях стоит ли это оставить, или убрать дайджест ссылок из ленты?

RabbitVCS - расширение для Nautilus для работы с Subversion

Раньше я использовал коллекцию скриптов nautilussvn, весьма неудобную в использовании.

Бродя по просторам сети наткнулся на отличную замену. Встречайте - RabbitVCS.

Возможности

  • Интеграция с Nautilus
  • Поддержка Subversion
  • Полная локализация
  • Поддержка командной строки
  • Доступны пакеты для различных дистрибутивов

Установка


Для Ubuntu Hardy, Intepid, Jaunty
deb http://ppa.launchpad.net/rabbitvcs/ppa/ubuntu hardy main


Для Ubuntu Karmic
sudo add-apt-repository ppa:rabbitvcs/ppa


Установка для других дистрибутивов описана на официальном сайте.

Работа

Несмотря на очень маленький номер версии работает достаточно стабильно, не падает.

Скриншоты

После установки внешний вид наутилуса преображается и выглядит примерно следующим образом (картинки кликабельны):


Диалог коммита выглядит вот так:


Контекстное меню:

Заключение

Достойный аналог TortoiseSVN под линукс.

25 November, 2009

Расширение Delicious Plugin идёт в массы

Google запустил сайт Chrome Extensions.

В данный момент сайт доступен только разработчикам расширений. Залил расширение, написал кратенькое описание. Жду запуска сайта. :)

А пока сайт закрыт для рядовых пользователей последнюю версию можно скачать тут.

P.S. А тем временем работа над расширением продолжается.

P.P.S. У расширения появилась своя страничка в сети: Project Home Page

24 November, 2009

Создание страницы настроек для расширений Google Chrome

В продолжение предыдущей заметки

Логично предположить, что у расширений могут быть настройки. В Google Chrome/Chromium для этого есть специальный API.

Для того, чтобы создать собственную страницу настроек необходимо сделать следующее:

1. Объявить в манифесте страницу настроек
{
  "name": "Delicious plugin", 
  "version": "0.2", 
  "background_page": "background.html", 
  "options_page": "options.html"
}

2. Реализовать страницу с настройками.

Страница с настройками - это обычная HTML-страничка. Для доступа к настройкам Google Chrome предоставляет объект localStorage, который умеет сохранять и возвращать значения.

С объектом localStorage работа идёт как с обычным hash.

<html>
<head>
<title>Delicious Bookmarks Options</title>
</head>
<script type="text/javascript">
    // Saves options to localStorage.
    function saveOptions() {
        var share = document.getElementById("share");
        localStorage["markPrivate"] = share.checked;

        // Update status to let user know options were saved.
        var status = document.getElementById("status");
        status.innerHTML = "Options Saved.";
        setTimeout(function() {
            status.innerHTML = "";
        }, 1500);
    }

    // Restores select box state to saved value from localStorage.
    function restoreOptions() {
        var share = localStorage["markPrivate"];

        if (!share) {
            return;
        }
        var shareCheckbox = document.getElementById("share");

        shareCheckbox.checked = share;
        
    }
</script>

<body onload="restoreOptions()">

<label for="share">Mark as Private</label>
<input type="checkbox" class="checkbox" name="share" id="share" />


<br>
<button onclick="saveOptions();">Save</button>
<div id="status">&nbsp;</div>
</body>
</html>

Чтение настроек

При инициализации страницы настроек необходимо проставить актуальные значения. Для этого на событе onload навешивается функция restoreOptions(), которая проставляет в пользовательском интерфейсе текущие настройки.

Запись настроек

Для сохранения настроек навешивается обработчик onclick для кнопки save - метод saveOptions.

Вот таким простым способом можно сохранять настройки расширения.

Документация

14 November, 2009

Расширение Delicious Bookmarks для Google Chrome/Chromium

Введение


Решил научиться писать собственные расширения для Google Chrome/Chromium.

За идею взял официальное расширение от Yahoo! для Firefox - Delicious Bookmarks.

Структура расширения


Расширение - файл с расширением .crx. На самом деле это просто ZIP-архив с файлом манифеста внутри.

Файл манифеста

{
"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
}

Манифест - файл в формате JSON.

Манифест состоит из следующих частей:

1. name - Имя расширения
2. version - Версия расширения
3. background_page - основной файл с расширением. Это обычный HTML-файл с разметкой.
4. permissions - разрешения для расширения. Указываем, что нам нужен доступ к вкладкам (tabs) и закладкам (bookmarks).
5. browser_action - указываем, что нужно отобразить на панели инструментов браузера.
6. content_scripts - Для доступа к DOM отображаемой страницы необходимы content scripts, данный блок регистрирует скрипт getDocumentSelection.js для всех http и https страниц.
7. options_page - страница с настройками.

Подробное описание файла манифеста есть на официальном сайте.

Описание работы расширения



Расширение работает следующим образом:
1. При клике на значок расширения вызывается обработчик addBookmark
2. Обработчик открывает коммуникационный порт и отправляет сообщение скрипту содержимого.
3. Скрипт содержимого забирает выделенный текст и отправляет назад расширению выделенный текст.
4. Расширение забирает выделенный текст, определяет настройки сохранения (Private/Public) и вызывает окно сохранения Delicious.

Реализация расширения



background.html:
<html>
<head>
<script type="text/javascript">
    function saveBookmark() {
        // Send our password to the current tab when clicked.
        chrome.tabs.getSelected(null, function(tab) {
            var port = chrome.tabs.connect(tab.id, {
                name : "deliciousBookmark"
            });
            port.postMessage( {
                action : 'getSelection'
            });
        });
    }

    function addBookmark(id, bookmark) {
        console.log("added bookmark:  " + bookmark);
        saveBookmark();
    }

    chrome.bookmarks.onCreated.addListener(addBookmark);
    console.log("Registered listener");

    function getShareStatus() {
        var markPrivate = localStorage["markPrivate"];
        var share = "yes";
        if (markPrivate == "true") {
            share = "no";
        }
        return share;
    }

    chrome.extension.onConnect.addListener(function(port) {
        console.assert(port.name == "deliciousBookmark");
        port.onMessage.addListener(function(msg) {
            var selection = msg.selection;
            chrome.tabs.getSelected(null, function(tab) {

                var url = encodeURIComponent(tab.url);

                if (!url || url === "") {
                    return;
                }

                var title = encodeURIComponent(tab.title);
                var description = encodeURIComponent(selection);

                var share = getShareStatus();

                var f = 'http://delicious.com/save?url=' + url + '&title=' + title + '&notes=' + description
                        + '&share=' + share + '&v=5&';
                window.open(f + 'noui=1&jump=doclose', 'deliciousuiv5',
                        'location=yes,links=no,scrollbars=no,toolbar=no,width=550,height=550');

            });
        });
    });

    chrome.browserAction.onClicked.addListener(saveBookmark);
</script>
</head>
</html>



getDocumentSelection.js

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
});
}
});
});




options.html

<html>
<html>
<head>
<title>Delicious Bookmarks Options</title>
</head>
<script type="text/javascript">
    // Saves options to localStorage.
    function saveOptions() {
        var share = document.getElementById("share");
        localStorage["markPrivate"] = share.checked;

        // Update status to let user know options were saved.
        var status = document.getElementById("status");
        status.innerHTML = "Options Saved.";
        setTimeout(function() {
            status.innerHTML = "";
        }, 1500);
    }

    // Restores select box state to saved value from localStorage.
    function restoreOptions() {
        var share = localStorage["markPrivate"];

        if (!share) {
            return;
        }
        var shareCheckbox = document.getElementById("share");

        shareCheckbox.checked = share;
        
    }
</script>

<body onload="restoreOptions()">

<label for="share">Mark as Private</label>
<input type="checkbox" class="checkbox" name="share" id="share" />


<br>
<button onclick="saveOptions();">Save</button>
<div id="status">&nbsp;</div>
</body>
</html>


Сборка расширения


В Chromium есть возможность собрать расширение.
1. Открываем адрес chrome://extensions/
2. В правом верхнем углу нажимаем Developer Mode.
3. На появившейся панели инструментов кликаем Pack Extension
4. Выбираем каталог, в котором находится расширение
5. Chromium автоматически собирает расширение в каталоге с исходниками расширения.

Заключение


Писать расширения для Google Chrome не просто, а очень просто.

Готовое и собранное расширение можно скачать здесь.

Дополнительная информация


  1. Google Chrome Extensions Developer's Guide
  2. Manifest file description
  3. Chrome Extensions API
  4. Extension Packaging


В следующей заметке опишу работу messaging для коммуникации расширения со скриптами содержимого.

02 November, 2009

Инвайты на Google Wave

Кому нужны инвайты на Google Wave, отписываемся в комментариях - поделюсь. UPD. Инвайты закончились, всем спасибо.

07 July, 2009

Двунаправленная ссылочная целостность

Здравствуйте.

Я считаю, что разработчику всегда стоит иметь представление о том, как работают низлежащие абстракции. Хотя бы потому, что часто абстракции бывают протекающими.

А веду я это к тому, что когда фреймворк вываливает стектрейс с несколькими вложенными (и, часто, для непосвященных непонятными) исключениями, то бывает непонятно, что с этим делать, особенно, если не знаешь, как реализована абстракция.

Иногда бывает нужно хранить двунаправленные связи между таблицами. Например, есть игрок и есть его статистика и нужно, чтобы каждая из сущностей знала про другую.

Вопрос - каким образом вставлять в базу данных такие сущности?

При вставке в таблицу в Player нам нужно уже иметь запись в таблице Statistics, а для вставки в таблицу Statistics нужно знать идентификатор игрока, к которому относится эта запись.

Выглядит замкнутым кругом, но решение есть.

Во-первых, вставки в обе таблицы должны происходить в рамках одной транзакции (как я уже писал, транзакция обеспечивает перевод базы данных из одного непротиворечащего состояния в другое непротиворечащее состояние).


К сожалению, этого недостаточно:

begin transaction;

INSERT INTO "Player"  VALUES (10, 20);
INSERT INTO "Statistics"  VALUES (20, 10);

commit;


выдаёт следующую ошибку:

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".


И снова кажется, что замкнутый круг не разорвать.

Но решение всё же есть.
Многие базы данных позволяют нарушать консистентность базы данных внутри транзакции. В данном случае можно "попросить" базу данных отложить проверку ссылочной целостности. На примере PostgreSQL рассмотрим, как это работает.

PostgreSQL


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


1 - Сделать все ограничения отложенными. Ограничения будут проверены в момент проведения фиксации транзакции (в строке 3)
2 - Явно делаем все ограничения немедленными. В данном примере это необязательно, так как в 3) они всё равно будут проверены.

После выполнения данного кода получаем:
Query returned successfully with no result in 24 ms.


Это уже полностью рабочий пример, чтобы это заработало нужно явно сделать ограничения отложенными - они должны быть объявлены как DEFERRED. Официальная документация к PostgreSQL гласит:
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.


Так что необходимо также модифицировать объявления ограничений:
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;



Данный пример был приведён для PostgreSQL.

Для других баз данных решения следующие:

Sybase


В Sybase есть переменная WAIT_FOR_COMMIT, которая управляет поведением проверки ограничений. По умолчанию она отключена. Кроме того, это поведение можно переопределить при объявлении можно указывать CHECK ON COMMIT. Подробнее можно посмотреть в официальной документации.

Oracle


Также, как и в PostgreSQL:
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:
DEFERRABLE or NOT DEFERRABLE
INITIALLY DEFERRED or INITIALLY IMMEDIATE

Constraints can be added, dropped, enabled, disabled, or validated. You can also modify a constraint's attributes.

Подробнее в официльной документации.

mySQL


К сожалению, mySQL не имеет возможности отложенной проверки ограничении, о чём в документации и сказано:
Deviation from SQL standards: 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.


Но это не конец света, так как можно отключить проверку внешних ключей:
SET foreign_key_checks = 0;
-- code goes here
SET foreign_key_checks = 1;


Чем эта информация может быть полезна для разработчика? Например, в случае, если ошибки и недоработки Hibernate покажут себя в виде стектрейса в логе(например, HHH-2248), то разработчик будет понимать причину ошибки и то, как реализовать work-around.

03 July, 2009

Opera и del.icio.us

Так получилось, что в последнее время я стал пользоваться небезызвестной оперой. В связи с этим я стал искать замену часто используемым расширениям, одно из них - delicious bookmarks. Сервис del.icio.us предоставляет букмарклеты для различных браузеров, которые позволяют сохранять закладки быстро и удобно. Вот только есть один недостаток - официальный плагин для Firefox в свойство Notes новой закладки добавляет выделенный текст со страницы, чего букмарклет не делает. Посмотрев исходник букмарклета, а это обычный javascript, я добавил небольшой кусочек кода, который восстанавливает справедливость и тоже добавляет в поле Notes текст, выделенный на странице. Ниже привожу изменённый код букмарклета:
javascript:(function(){f='http://delicious.com/save?url='+encodeURIComponent(window.location.href)+'&title='+encodeURIComponent(document.title)+'¬es='+encodeURIComponent(document.getSelection())+'&v=5&';a=function(){if(!window.open(f+'noui=1&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()}})()

06 June, 2009

Spring Roo (часть 2)

Архитектура сгенерированного приложения

Spring Roo активно использует аспекты. В качестве реализации аспектов была взята библиотека aspectj. Большая часть сгенерированного кода попадает в отдельные файлы-аспекты. Создадим простой класс:
new persistent class jpa -name ~.domain.Action -testAutomatically
add field string name -notNull -sizeMin 1 -sizeMax 80
add field string description -sizeMax 1024
Spring Roo создаст нам следующие файлы:
  • Action.java Класс содержит только полезную с точки зрения назначения класса информацию. По сути, то, что вводит разработчик в консоль Roo аккумулируется здесь. Весь дополнительный код разносится по другим классам.
    @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;
    }
    
    1) Класс является JPA-сущностью. 2) Аннотация @RooJavaBean говорит, что этот класс - обычный Java-бин. 3) Аннотация @RooToString говорит, что у этого класса есть перегруженный метод toString(). 4) Аннотация @RooEntity говорит что класс является JPA-сущностью.
  • Action_Roo_Configurable.aj Данный аспект говорит, что класс Action является конфигурируемым через Spring.
    privileged aspect Action_Roo_Configurable {
    declare @type: Action: @org.springframework.beans.factory.annotation.Configurable;
    }
    
  • Action_Roo_Entity.aj В данном классе собраны все методы DAO для работы с классом Action.
    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 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 Action.findActionEntries(int firstResult, int maxResults) {
    return new Action().entityManager.createQuery("select o from Action o").setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }
    
    }
    
    Как легко видеть, этот аспект добавляет классу Action CRUD-методы, поля id и version.
  • Action_Roo_Finder.aj Этот аспект аккумулирует все finder-методы, созданные через консоль Spring Roo.
    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;
    }
    
    }
    
  • Action_Roo_JavaBean.aj Для уменьшения загромождения кода ненужными методами для доступа к полям Spring Roo выносит их в отдельный аспект.
    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;
    }
    
    }
    
  • Action_Roo_Plural.aj В данном аспекте можно определить, как созданная сущность выглядит во множественном числе.
    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;
    }
    
    }
    
    
  • Action_Roo_ToString.aj Аспект, переопределяющий toString()
    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();
    }
    
    }
    
    
  • ActionEditor.java Кроме того, при создании контроллеров для работы дополнительно создается ActionEditor
    import org.springframework.roo.addon.property.editor.RooEditor;
    
    @RooEditor(providePropertyEditorFor = Action.class)
    public class ActionEditor {
    }
    
    
  • ActionEditor_Roo_Editor.aj Аспект с реализацией.
    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)); 
    }
    
    }
    
    

А где же equals()/hashCode()?

Легко заметить, что Spring Roo забыл ещё про один важный аспект - про реализацию методов equals() и hashCode(). К релизу, я думаю, эта недоработка будет исправлена. Как видно, код скрипта для генерации весьма и весьма лаконичен, а получающий java-код наглядно показывает всю многословность языка Java.

Apache Maven integration

Spring Roo при создании проекта создаёт pom.xml, хорошо знакомый всем работавшим с maven. Кроме того, консоль позволяет добавлять и удалять зависимости, не правя файл вручную.

Расширения

Spring Roo поддерживает расширения, что ещё больше даёт возможностей. На сегодняшний день написано уже как минимум одно расширение - Sitemesh Addon.

Критика

В первую очередь критика относится к текущему релизу, а так как он ещё в глубокой альфе, то все может измениться к официальному релизу.
  1. Нет возможности подставлять свои JSP-шаблоны для сгенерированного CRUD
  2. В сгенерированном коде есть ошибки, например, если заводим поле типа флоат, создаём контроллер, то при редактировании сущности возможна следующая ошибка: вводим достаточно большое число (1234567), сохраняем сущность, открываем на редактирование. В поле, хранящем флоат получаем 1.23456E7, и после этого невозможно сохранить сущность, так как получаем ошибку валидации.
  3. Ещё одна ошибка связана с отношением один ко многим. Создаём пару сущностей с таким отношением. Запускаем, пробуем создать сущность. Roo умничает, если нет связанных сущностей в БД, то он даже не генерирует <select>

Вывод

В целом, Spring Roo достойное начинание, но пользоваться им в Production можно будет ещё нескоро.

02 June, 2009

Введение в Spring Roo

Недавно компания Spring Source презентовала новый продукт - Spring Roo. Цель проекта - повысить продуктивность Java-разработчиков. Почитав обзоры, а также потрогав его руками можно сделать вывод о том, что это, в некотором виде, альтернатива AppFuse и Grails.

Описание Spring Roo

  • 100% программирование на Java, предлагающее разработчикам известную, развитую и популярную платформу разработки.
  • Прозрачные, надёжные и продуктивные сервисы среды разработки, такие как помощник кода, отладчики, визуальные отчёты об ошибка и т.д.
  • Экстремально эффективная производительность во время выполнения, безопасность типов и отсутствие зависимостей на Roo во время выполнения.
  • Автоматическая архитектура с использование лучших практик Spring Framework 3
  • Структура проекта основана на Maven2.
  • Работа с БД основана на JPA (например, через Hibernate), 100% совместимость с JPA и переносимость реализации.
  • Встроенная поддержка конфигурирования баз данных, с автоматической настройкой JDBC для большинства популярных баз данных.
  • Прозрачная поддержка внедрения зависимостей (dependency injection) и методов сохранения для всех сущностей, включая полученные через JPA.
  • Поддержка валидации бинов (JSR 303), включая распространение ограничений вплость до DDL базы данных.
  • Автоматические интеграционные тесты на JUnit, которые создаются на базе возможностей интеграционного тестирования Spring Framework.
  • Автоматическая поддержка REST на уровне бэкэнда.
  • Автоматизированные тесты для веб-слоя с использованием Selenium.
  • Динамическое создание finder-методов на JPA QL для сущностей безо всякого кодирования.
  • Интеграция Spring Security, включая безопасность адресов с установкой в одну строку ("install security")
  • Поддеркжа Spring Web Flow одной командой ("install web flow")
  • Поддержка мгновенной отправки почтовых сообщение - даже через удалённые SMTP сервера, как GMail.
  • Встроенная поддержка конфигурирования log4j
  • Использование URL rewriting которое позволяет оставаться ссылкам чистыми и поддерживающими REST.
  • Лёгкое ручное создание веб-контроллеров
  • Поддержка полного цикла жизни приложения с сохранением высокой продуктивности.
  • Использование встроенного сервлет контейнера Tomcat.
  • Полная интеграция с Eclipse и SpringSource Tool Suite
  • Лёгкая в использовании, с поддержка автодополнения, подсказок и контекста командная строка
  • Поддержка автоматизации через скрипты
  • Apache Ant и Metro

    Столкнулся с такой проблемой - в некоторых случаях приложение, собранное антом не работает. Конкретно - не работает десереализация запросов на веб-сервис. Получаемый запрос содержит null в значениях полей. Опытным путём было выяснено, что на это влияет используемая версия Java. Под 1.5 работает корректно, под 1.6 - не работает. Нашли ошибку в метро, связанную именно с версией JDK и на этом успокоились. Но было одно исключение из правил. У одного из разработчиков под 1.5 тоже не работало. Как выяснилось позже, виноват был во всё Ant. Metro создавал файл package-info.java, в котором было примерно следующее содержание: @javax.xml.bind.annotation.XmlSchema(namespace = "http://z.y.x.com", elementFormDefault=javax.xml.bind.annotation.XmlNsForm.UNQUALIFIED) package com.x.y.z; Ант, в свою очередь, должен был этот файл компилировать. А в реальности этого не происходило, поэтому Metro не мог корректно отобразить XML-запрос на объекты доменной области. Решение было немного кривым, но при этом поразительно простым - принудительный вызов javac на файлах package-info.java.

    30 May, 2009

    Использование модификатора protected на полях в базовом классе

    Недавно задали мне вопрос - что я думаю об использовании модификатора protected для членов базового класса? Под этим вопросом кроются более серьёзные вещи. Например, любой член класса, имеющий модификатор protected, расширяет интерфейс базового класса для подклассов - подклассы имеют доступ ко всем protected и public методам этого поля. Иногда это сделано специально, например, для того, чтобы подклассы могли иметь доступ к какой-либо функциональности. Однако, если подкласс будет использовать protected поля неправильно (например, он может установить его в null, нарушив, таким образом, поведение базового класса), то это не принесёт пользы. Поразмыслив над вопросом я пришел к следующему, достаточно логичному и широкоизвестному выводу - необходимо давать наиболее закрытый доступ, какой можно. То есть, если можно объявить член класса как private, то необходимо сделать его private. Если в подклассах необходим доступ к члену класса, то необходимо проанализировать, какие методы нужны в подклассах и открыть только их через делегирование. Если набор методов заранее неизвестен, то можно сделать protected getter для поля.

    26 May, 2009

    Сферический программист в вакууме и абстрактное понятие качества кода в условиях Fixed-Price проектов (часть 2)

    Причин для написания плохого кода может быть несколько. Некоторые из них лежат на поверхности:
    1. Отсутствие опыта
    2. Невнимательность
    3. Некомпетентность
    4. Немотивированность
    Отсутствие опыта - казалось бы, самая простая причина проблемы; решение проблемы выглядит очевидным - со временем, разработчик будет писать код лучше. Но это ошибочное мнение. Бывает, люди с опытом более 5-10 лет разработки коммерческих приложений пишут настолько плохой код, что иногда кажется, что они это нарочно. Решать эту проблему необходимо постоянным указанием на ошибки. Но это решит только часть проблемы - человек узнает, как писать код в конкретных ситуациях. Но не всякий разработчик (к сожалению, по опыту знаю) сможет экстраполировать знание на все подобные проблемы способ решения. Невнимательность - через это проходят почти все. Почти каждый сможет вспомнить случай, когда не был сделан svn add перед коммитом. Эта проблема решается обычно самомотивацией человека на хороший код. Впрочем, команда и сервер непрерывной интеграции тоже прилагают усилия для того, чтобы человек проверил ещё раз то, что он хочет выложить в репозиторий. Некомпетентность - эта причина пересекается с недостатком опыта, но, при этом отлична от неё. Адекватный разработчик, на мой взгляд, должен стремиться к компетентности в той области, в которой он работает. Но этого, к сожалению часто не происходит. На мой взгляд это из-за следующей причины. Немотивированность. Самое большое зло, которое может быть. Причины недостаточной мотивации разработчика могут быть весьма разнообразными - не выспался, дома проблемы, зарплата низкая, задача скучная и неинтересная. Список можно продолжать до бесконечности. Решение этой проблемы кроется, в первую очередь, в качественном управлении. Менеджер должен быть тонким и хорошим психологом, чтобы видеть или чувствовать все эти проблемы и предотвращать их.

    Сферический программист в вакууме и абстрактное понятие качества кода в условиях Fixed-Price проектов (часть 1)

    В последнее время всё чаще задумываюсь о том, почему разработчики пишут плохой и некачественный код. Под кодом я подразумеваю не только сам код, но и, в том числе, архитектуру и дизайн. Плохим кодом я считаю следущий:
    1. Невыполняющий функциональные требования
    2. Невыполняющий нефункциональные требования
    3. Непокрытый тестами
    4. Плохо тестируемый
    5. Плохо поддерживаемый
    6. С багами
    7. Неоптимальный
    Если код не выполняет функциональные требования, то этот код вообще ничего не стоит. Заказчику такой код не нужен, именно поэтому этот код плохой. На него были потрачены ресурсы, но при этом сам код не имеет никакой ценности. Если код не выполняет нефункциональные требования, то это говорит о том, что либо дизайн, либо реализация, не учли требования и это тоже не добавляет удовлетворённости конечному пользователю. Часто здесь можно провести рефакторинг, но бывает и так, что архитектурные ошибки не дают в принципе выполнить нефункциональные требования, либо их реализация требует очень больших временных затрат. Если код не покрыт тестами, то этот код не работает. Эта аксиома должна всегда быть перед глазами разработчика. Как говорится, самая большая проблема с компьютерами это то, что они делают не то, что разработчик подразумевал, а то, что он написал. Именно поэтому тесты - гарантия того, что код выполняет то, что подразумевает разработчик. Хотя здесь тоже есть проблема, что разработчик не понимает, что именно ему нужно от кода, либо не знает, как протестировать нужную ему функциональность. Плохо тестируемый код. Такой код часто встречается, когда не используется TDD, в связи с этим разработчик не понимает или не знает, что именно он хочет от кода. Также, плохо тестируемый код это признак того, что код имеет не самую лучшую архитектуру (например, высокий coupling) Плохо поддерживаемый код это любой код, который сложно поддерживать. Сюда относятся любые ошибки в коде, которые осложняют поддержку - нет единого стиля форматирования, неясные имена переменных, методов, классов, плохая декомпозиция, отсутствие или наличие устаревших тестов, комментариев, документации, или, что ещё хуже ошибки в комментариях, документации, плохая архитектура (как её отсутствие, так и overdesigned). Все эти проблемы в конечном итоге влияют на качество проекта и на удовлетворённость заказчика. Код с багами. Логично, что это плохой код, и это, к сожалению, часто встречающийся код. По статистике, на 1000 строк кода приходится 10-12 багов. К сожалению, этот показатель меняется в зависимости от различных факторов (компетентность разработчика, сроки, сложность архитектуры, используемый язык и технологии и т.д.) и часто в сторону ухудшения. Неоптимальный код не всегда плохой, при условии, что он выполняет функциональные и нефункциональные требования, а также лёгок в поддерживании. Продолжение следует...

    23 May, 2009

    Google Application Engine

    Посмотрел на Google App Engine (GAE). При своей цене (= бесплатно) это очень хороший сервис. GAE - это инфраструктура для запуска веб-приложений на серверах Google. То, что сейчас модно называть cloud computing. На презентации платформы в 2008 году Google объявил только о поддержке Python. Год спустя, в апреле 2009 года Google заявил о поддержке Java, правда с ограничениями. Собственно, поддержка Java и была тем самым толчком, что я решил поближе посмотреть на платформу.

    Возможности

    Google App Engine предоставляет следующие возможности как платформа:
    • масштабируемость
    • кластеризация
    • он-лайн мониторинг
    • балансировку нагрузки
    • безопасность
    • горизонтальное масштабирование базы данных
    • отказоустойчивость

    Ограничения

    Как и любое бесплатное решение у платформы есть ограничения. На данный момент эти ограничения следующие:
    • Ограниченный набор Java API
    • Нереляционное хранилище данных
    • Ограничение доступа к другим компьютерам
    • Из доступных языков: Python, Java и JVM-based
    • Ограничения на используемые ресурсы

    Технологии

    App Engine Services

    База данных

    Это небезызвестный BigTable. Для работы с ним используется GQL. Для Java-реализации используется JDO с ограниченной поддержкой JPA.

    Применение

    Уже сейчас существует много приложений, которые размещены на серверах Google. Многие из них доступны в галерее приложений App Engine На мой взгляд, использование нереляционной модели данных накладывает свои ограничения на тип проектируемых приложений, так как не всегда возможна полная денормализация данных.

    Впечатления от использования SDK

    Обзор не был бы полным, если бы я не попробовал написать простенькое приложение для платформы. Для этого необходимо зарегистрироваться на App Engine и запросить поддержку Java. Google объявил, что попробовать платформу можно только первым 10000 разработчикам. Судя по всему, ещё можно успеть, так как я получил доступ в течении 15 минут (приходит ключ по SMS). Подтверждение на поддержку Java пришло в течении суток. Google предоставляе SDK для работы с платформой. В SDK входят скрипты для создания приложения, загрузки их на сервера Google и API для доступа к вышеперечисленным сервисам платформы. Кроме того, при разработке используются заглушки для сервисов (например, для User Service API), которые позволяют локально писать и отлаживать приложение. Компания SpringSource объявила о поддержке Grails на платформе Google App Engine. Это не может не радовать, но первые впечатления были смазаны, так как плагин appengine для Grails ещё нужно дорабатывать. Например, кодогенератор заточен для gorm, который сделан поверх hibernate, но так как appengine не поддерживает hibernate, для генерации CRUD приходится делать следующие пассы: включать hibernate плагин, генерировать CRUD, отключать hibernate плагин. Надеюсь, в следующей версии appengine плагина эта проблема будет решена. Остальные проблемы можно почитать как в комментариях к анонсу, так и в джире проекта.

    Альтернативные платформы

    Самым известным, наверно, является Amazon EC2, а замыкает тройку Microsoft с их Windows Azure. Amazon не предоставляет бесплатной дозы, в отличии от Microsoft и Google. При этом Amazon предоставляет shell-доступ к серверам. Сравнивать в целом платформы не имеет смысла, поэтому не буду.

    02 May, 2009

    Eclipse и его расширения

    Многие ругают Eclipse за то, что там нет многого из того, что есть в коробке у Jetbrains Intellij IDEA. Мне всегда казалось это не очень корректным, так как Eclipse в первую очередь платформа, а уже потом - среда для Java разработки.
    Решил собрать воедино весь список используемых мною расширений для Eclipse.
    1. Subclipse - без этого никуда. Это расширение позволяет работать с системой контроля версий Subversion не выходя из Eclipse.
    2. m2eclipse - Расширение для работы с maven. Графический редактор pom.xml позволяет избавиться от редактирования pom.xml вручную. В качестве приятного дополнения - возможность посмотреть графически список зависимостей проекта (вместе с транзитивными), а так же увидеть ситуации, когда 2 разные библиотеки зависят от разных версий одной и той же библиотеки.
    3. Google Plugin for Eclipse - официальное расширение от Google для работы с Google Web Toolkit и Google Application Engine.
    4. Eclipse-CS - Нет, это не запускалка Counter Strike, а всего лишь расширение, интегрирующее статический анализатор исходного кода Checkstyle в Eclipse. Проверяет правильность форматирования кода согласно заданным правилам. Обязательное расширение при работе в команде.
    5. Spring IDE - расширение от команды SpringSource для работы с проектами, использующими Spring, в том числе Spring AOP и Spring Webflow.
    6. XMind - отличное расширение, предоставляющее mind-mapping возможности в Eclipse.
    7. JBoss Tools - наборо расширений для работы с продуктами JBoss - Hibernate, JBoss AS, Drools, jBPM, JSF, (X)HTML, Seam, Smooks, JBoss ESB, JBoss Portal...
    8. EclEmma - интеграция отличной библиотеки EMMA в Eclipse. EMMA - приложение для подсчёта покрытия кода тестами. Must-have расширение.
    9. TPTP - Под этой странной аббревиатурой скрывается Test & Performance Tools Platform. Набор расширений для тестирования и профилирования приложений.
    10. PMD - Интеграция библиотеки PMD, которая выполняет статический анализ кода на предмет возможных ошибок в коде.
    11. WTP - Web Tools Platform - официальный набор расширений для разработки Web и Java EE приложений.
    12. Groovy & Grails - расширение для работы с языком Groovy и фреймворком Grails.
    13. eGit - расширение предоставляет возможность работы с распределенной системой контроля версий git.
    14. Eclipse SQL Explorer - тонкий SQL-клиент, который позволяет просматривать различные JDBC-совместимые базы данных.
    Очень много расширений есть на Eclipse Plugin Central

    Также, все плагины, на которые я обращаю внимание можно найти на del.icio.us

    26 April, 2009

    Критерии выбора блокировок

    Так как использование блокировок по-разному влияет на производительность, необходимо выбирать, в каких случаях какой вид блокировок лучше использовать.
    Условие Пессимистичная Оптимистичная Сверх-оптимистичная
    Конфликты часты +
    Конфликты редки +
    Данные только добавляются (например, журнал) +
    Данные только читаются (справочники) +
    Критерии выбора блокировок

    24 April, 2009

    Транзакции в реляционных базах данных

    При работе в многопользовательском окружении может возникнуть ситуация, когда разные пользователи или системы обновляют одни и те же данные. Это может привести к ситуации, когда один из пользователей изменит данные, которые уже изменял другой пользователь и приведёт либо к потере данных, либо к переводу данных в неконсистентное состояние. Для решения этой проблемы были придуманы транзакции. Целью транзакции является создание абстракции того, что каждый пользователь - единственный в системе.

    Характеристики транзакций

    В конце 1970-х Джим Грей выделил следующие характеристики транзакций:
    • Атомарность (Atomicity)
    • Непротиворечивость (Consistency)
    • Изолированность (Isolation)
    • Долговечность (Durability)
    Атомарность означает, что все действия, производимые в рамках одной транзакции атомарны. Если транзакция заканчивается не успешно, то все изменения, сделанный в рамках этой транзакции откатываются назад. Если транзакция заканчивается успешно, то все её изменения сохраняются. Непротиворечивость означает, что транзакция переводит базу данных из одного непротиворечивого состояния в другое непротиворечивое состояние. При этом, внутри транзакции база данных может находиться в противоречивом состоянии. Изолированность означает, что во время работы одной транзакции ей недоступны изменения, происходящие в другой транзакции. Под долговечностью подразумевают, что после окончания транзакции все изменения, сделанные транзакцией фиксируется навсегда, то есть, если происходит программно-аппаратный сбой, то после восстановления системы в рабочее состояние нет потерянных изменений.

    Уровни изоляции транзакций

    При параллельном выполнении нескольких транзакций могут возникнуть следующие проблемы:
    • Потерянное обновление
    • "Грязное" чтение
    • Неповторяющееся чтение
    • Фантомная вставка
    Потерянное обновление может возникнуть в ситуации, когда две транзакции обновляют одни и те же данные. "Грязное" чтение возникает, когда одна транзакция видит несохранённые изменения, сделанные другой транзакцией. Неповторяющееся чтение - это ситуация, когда в рамках одной транзакции один и тот же запрос на выборку возвращает разные результаты. Фантомная вставка - ситуация, похожая на неповторяющее чтение - заключается в том, что результаты одной и той же выборки в рамках одной транзакции имеют разное количество возвращаемых строк. Стандарт SQL-92 определяет следующие уровни изоляции транзакции:
    1. READ_UNCOMMITED
    2. READ_COMMITED
    3. REPEATABLE_READ
    4. SERIALIZABLE
    READ_UNCOMMITED - позволяет другим транзакциям видеть несохранённые изменения READ_COMMITED - транзакция во время выполенения видит только зафиксированные изменения. В большинстве баз данных это уровень изоляции по умолчанию. REPEATABLE_READ - данный уровень изоляции решает проблему неповторяющего чтения. SERIALIZABLE - данный уровень изоляции выполняет все транзакции последовательно. Это самый надёжный уровень изоляции, но, при этом, и наименее производительный. Каждый уровень решает часть вышеперечисленных проблем.
    Проблема Уровень изоляции Потерянное обновление Грязное чтение Неповторяющееся чтение Фантомная вставка
    READ_UNCOMMITED невозможно возможно возможно возможна
    READ_COMMITED невозможно невозможно возможно возможна
    REPEATABLE_READ невозможно невозможно невозможно возможна
    SERIALIZABLE невозможно невозможно невозможно невозможна

    22 April, 2009

    Кратко о блокировках

    Блокировка - в реляционных базах данных это установка метки на запись, что запись заблокирована для изменений. Существует два вида блокировок - оптимистичная и пессимистичная.

    Оптимистичная блокировка

    При оптимистичной блокировке на базе данных реальной блокировки не происходит. Вместо этого используется следующий подход - если во время выполнения транзакции она изменяет данные, которые были изменены после её начала, то транзакция прерывается с исключением. Использование оптимистичных блокировок позволяет избежать взаимных блокировок (dead-lock). Для реализации оптимистичной блокировки часто используется версионирование данных - в таблицу добавляется колонка, которая хранит текущую версию. При выполнении update в запросе в секции where передается версия данных, которая была забрана на изменение. Если update вернул 0 изменённых строк, значит данные были уже изменены и транзакцию необходимо запускать заново. Вместо версии можно хранить время последнего изменения данных.

    Пессимистичная блокировка

    При пессимистичной блокировке для записи ставится эксклюзивная блокировка на уровне базы данных, запрещая таким образом доступ к данным из других транзакций. Существует несколько видов пессимистичных блокировок:
    1. блокировка при чтении
    2. блокировка при записи
    При блокировке при чтении запись блокируется когда она запрашивается из базы данных. Недостаток метода в том, что таким образом можно заблокировать даже те данные, которые не изменяются в рамках текущей транзакции. При блокировке при записи блокировка даных происходит при их обновлении в базе данных до конца текущей транзакции. Блокировка с данных снимается либо при коммите, либо при откате транзакции.

    Сравнение оптимистичных и пессимистичных блокировок

    При разработке программного обеспечения необходимо выбирать стратегию блокировок данных. При этом следует учитывать следующее:
    1. Если ситуация обновления одних и тех же данных в один момент времени относительно редка, то выгоднее использовать оптимистичную блокировку. В этом случае не будут происходить дорогая операция блокировки ресурсов.
    2. Если же возможность возникновения ситуации обновления одних и тех же данных достаточно высока, то лучше использовать пессимистичную блокировку, это снизит количество прерванных транзакций.
    3. Также следует учитывать, что при оптимистичной блокировке в случае прерывания транзакции её нужно запускать заново.

    21 April, 2009

    Практический опыт использования фолксономии в условиях неограниченной свободы расстановки меток (часть 2)

    В продолжение предыдущей заметки . Для работы с del.icio.us я использую официальный плагин от Yahoo!. Он предоставляет следующие возможности: 1. Синхронизация закладок из Firefox с del.icio.us 2. Переопределение стандартного менеджера закладок. 3. Отслеживание изменений на del.icio.us. 4. Интерфейс для отображения и поиска закладок в панели браузера. 5. Панель инструментов для работы с закладками. Из всех вышеперечисленных возможностей раньше пользовался только вторым пунктом. Это очень удобно - выделяешь на странице кусок текста и клацаешь ctrl+b. Появляется окошечко с уже заполненной ссылкой, заголовком и описанием ссылки. В поле описания попадает выделенный текст. После этого расставляешь метки и жмёшь кнопку save. Мне этого хватало и я был доволен. Недавно я для себя открыл следующую вещь - в панели инструментов Delicous Toolbar можно выбрать вид Favorites Tag Views и настроить избранные теги, которые будут отображаться на панели. Выглядит это следующим образом: При щелчке на тег показыватся список закладок, помеченных избранными тегами. Очень удобно.

    19 April, 2009

    Практический опыт использования фолксономии в условиях неограниченной свободы расстановки меток

    Уже более двух лет я использую del.icio.us. Недавно возник вопрос - а вот каким образом можно наиболее эффективно использовать теги? Для себя я придумал вот такой вариант использования - сделал tag bundle Actions, в который поместил теги, диктующие дальнейшие действия. Для себя я выделил следующие теги: 2rss - закладки с этим тегом я позже добавляю в свою RSS-читалку. 2check - этим тегом я помечаю софт или библиотеки, которые хотелось бы изучить и попробовать на деле. 2read - статьи, которые нужно прочитать и/или изучить. 2download - то, что нужно скачать. Соответственно, после того, как я действие было выполнено, я удаляю тег действия и обновляю теги на странице (особенно для страниц, помеченных как 2read). Для себя я вынес следующие правила расстановки тегов: 1. Теги должны быть в одном числе. Я ставлю в единственном. Кому-то покажется, что лучше во множественном лучше, пусть. Главное - быть консистентным. 2. Теги нужно ставить понятные самому себе, а не те, что предлагает народ - пользоваться-то в первую очередь потом тегами придется мне, а не народу. 3. Расставлять не более 10 тегов на страницу, иначе релевантность тегов уменьшается. 4. Теги должны отражать содержимое страницы.